From d841f5e17897be5ef6aef5031b8a03ed3d8fd8dd Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Thu, 8 Jul 2021 15:29:30 -0400 Subject: [PATCH 01/72] Ship a .framework version of Mono (#53370) Additionally ship Mono on iOS/tvOS/MacCatalyst as a .framework bundle Fixes #42846 --- eng/liveBuilds.targets | 2 + eng/pipelines/common/global-build-job.yml | 8 +++- eng/pipelines/mono/templates/build-job.yml | 12 +++++- eng/pipelines/runtime-staging.yml | 2 +- .../Microsoft.NETCore.App.Runtime.props | 12 ++++++ src/mono/mono.proj | 38 +++++++++++++++++ src/mono/mono/mini/CMakeLists.txt | 41 +++++++++++++++++++ 7 files changed, 112 insertions(+), 3 deletions(-) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 01a0c6aac679a..fb05c25a395a1 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -109,6 +109,8 @@ true + + diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index 96c116eb87272..84f1786bd2171 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -83,6 +83,12 @@ jobs: ${{ if ne(parameters.isOfficialBuild, true) }}: value: '' + - name: _buildDarwinFrameworksParameter + ${{ if in(parameters.osGroup, 'iOS', 'tvOS', 'MacCatalyst')}}: + value: /p:BuildDarwinFrameworks=true + ${{ if notin(parameters.osGroup, 'iOS', 'tvOS', 'MacCatalyst')}}: + value: '' + - name: _richCodeNavigationParam ${{ if eq(parameters.enableRichCodeNavigation, true) }}: value: /p:EnableRichCodeNavigation=true @@ -134,7 +140,7 @@ jobs: displayName: Install native dependencies # Build - - script: $(_sclEnableCommand) $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) + - script: $(_sclEnableCommand) $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) $(_buildDarwinFrameworksParameter) displayName: Build product ${{ if eq(parameters.useContinueOnErrorDuringBuild, true) }}: continueOnError: ${{ parameters.shouldContinueOnError }} diff --git a/eng/pipelines/mono/templates/build-job.yml b/eng/pipelines/mono/templates/build-job.yml index ef9af237168b7..bdb15a09a665e 100644 --- a/eng/pipelines/mono/templates/build-job.yml +++ b/eng/pipelines/mono/templates/build-job.yml @@ -63,6 +63,8 @@ jobs: value: '' - name: msCorDbi value: '+mono.mscordbi' + - name: darwinFrameworks + value: '' - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: - name: officialBuildIdArg value: '/p:officialBuildId=$(Build.BuildNumber)' @@ -76,15 +78,23 @@ jobs: - ${{ if eq(parameters.osGroup, 'tvOS') }}: - name: osOverride value: -os tvOS + - name: darwinFrameworks + value: /p:BuildDarwinFrameworks=true - ${{ if eq(parameters.osGroup, 'tvOSSimulator') }}: - name: osOverride value: -os tvOSSimulator + - name: darwinFrameworks + value: /p:BuildDarwinFrameworks=true - ${{ if eq(parameters.osGroup, 'iOS') }}: - name: osOverride value: -os iOS + - name: darwinFrameworks + value: /p:BuildDarwinFrameworks=true - ${{ if eq(parameters.osGroup, 'iOSSimulator') }}: - name: osOverride value: -os iOSSimulator + - name: darwinFrameworks + value: /p:BuildDarwinFrameworks=true - ${{ if eq(parameters.osGroup, 'Android') }}: - name: osOverride value: -os Android @@ -136,7 +146,7 @@ jobs: # Build - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) + - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(darwinFrameworks) displayName: Build product - ${{ if eq(parameters.osGroup, 'windows') }}: - script: build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 666641354bf5a..f58caca2899de 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -119,7 +119,7 @@ jobs: jobParameters: testGroup: innerloop nameSuffix: AllSubsets_Mono - buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunAOTCompilation=true /p:MonoForceInterpreter=true + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true timeoutInMinutes: 180 condition: >- or( 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 4eea2cf850fa0..28ac4374be2ae 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props @@ -70,6 +70,18 @@ runtimes/$(RuntimeIdentifier)/native/include/%(RecursiveDir) + + runtimes/$(RuntimeIdentifier)/native/Mono.release.framework/%(RecursiveDir) + + + + runtimes/$(RuntimeIdentifier)/native/Mono.debug.framework/%(RecursiveDir) + + diff --git a/src/mono/mono.proj b/src/mono/mono.proj index c26eb9f4e2e54..cc9fc41b54629 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -198,6 +198,7 @@ <_MonoCMakeArgs Include="-DCMAKE_BUILD_TYPE=$(Configuration)"/> <_MonoCMakeArgs Condition="'$(CMakeArgs)' != ''" Include="$(CMakeArgs)"/> <_MonoCMakeArgs Condition="'$(MonoEnableLLVM)' == 'true'" Include="-DLLVM_PREFIX=$(MonoLLVMDir.TrimEnd('\/'))" /> + <_MonoCMakeArgs Condition="'$(BuildDarwinFrameworks)' == 'true'" Include="-DBUILD_DARWIN_FRAMEWORKS=1" /> <_MonoCMakeArgs Include="-DGC_SUSPEND=$(MonoThreadSuspend)" /> <_MonoCMakeArgs Include="-DMONO_LIB_NAME=$(MonoLibName)" /> <_MonoCMakeArgs Include="-DMONO_SHARED_LIB_NAME=$(MonoSharedLibName)" /> @@ -508,6 +509,7 @@ + @@ -773,6 +775,42 @@ $(RuntimeBinDir)cross\$(PackageRID)\opt$(ExeExt) <_MonoIncludeArtifacts Include="$(MonoObjDir)out\include\**" /> + <_MonoRuntimeArtifacts Condition="'$(MonoComponentsStatic)' != 'true' and Exists('$(MonoObjDir)out\lib\Mono.release.framework')" Include="@(_MonoRuntimeComponentsSharedFilePath)"> + $(RuntimeBinDir)\Mono.release.framework\%(_MonoRuntimeComponentsSharedFilePath.Filename)%(_MonoRuntimeComponentsSharedFilePath.Extension) + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.release.framework') and !Exists('$(MonoObjDir)out\lib\Mono.release.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.release.framework\Mono.release"> + $(RuntimeBinDir)\Mono.release.framework\Mono + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.release.framework') and Exists('$(MonoObjDir)out\lib\Mono.release.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.release.framework\Versions\Current\Mono.release"> + $(RuntimeBinDir)\Mono.release.framework\Mono + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.release.framework')" Include="$(MonoObjDir)out\lib\Mono.release.framework\Mono.release.dwarf"> + $(RuntimeBinDir)\Mono.release.framework\Mono.dwarf + + <_MonoRuntimeArtifacts Condition="'$(MonoComponentsStatic)' != 'true' and Exists('$(MonoObjDir)out\lib\Mono.debug.framework')" Include="@(_MonoRuntimeComponentsSharedFilePath)"> + $(RuntimeBinDir)\Mono.debug.framework\%(_MonoRuntimeComponentsSharedFilePath.Filename)%(_MonoRuntimeComponentsSharedFilePath.Extension) + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.debug.framework') and !Exists('$(MonoObjDir)out\lib\Mono.debug.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.debug.framework\Mono.debug"> + $(RuntimeBinDir)\Mono.debug.framework\Mono + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.debug.framework') and Exists('$(MonoObjDir)out\lib\Mono.debug.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.debug.framework\Versions\Current\Mono.debug"> + $(RuntimeBinDir)\Mono.debug.framework\Mono + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.debug.framework')" Include="$(MonoObjDir)out\lib\Mono.debug.framework\Mono.debug.dwarf"> + $(RuntimeBinDir)\Mono.debug.framework\Mono.dwarf + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.release.framework') and !Exists('$(MonoObjDir)out\lib\Mono.release.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.release.framework\Info.plist"> + $(RuntimeBinDir)\Mono.release.framework\Info.plist + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.release.framework') and Exists('$(MonoObjDir)out\lib\Mono.release.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.release.framework\Versions\Current\Resources\Info.plist"> + $(RuntimeBinDir)\Mono.release.framework\Info.plist + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.debug.framework') and !Exists('$(MonoObjDir)out\lib\Mono.debug.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.debug.framework\Info.plist"> + $(RuntimeBinDir)\Mono.debug.framework\Info.plist + + <_MonoRuntimeArtifacts Condition="Exists('$(MonoObjDir)out\lib\Mono.debug.framework') and Exists('$(MonoObjDir)out\lib\Mono.debug.framework\Versions')" Include="$(MonoObjDir)out\lib\Mono.debug.framework\Versions\Current\Resources\Info.plist"> + $(RuntimeBinDir)\Mono.debug.framework\Info.plist + <_MonoRuntimeBuildArtifacts Include="$(MonoObjDir)\build\**" /> <_MonoRuntimeArtifacts Condition="'$(_MonoIncludeInterpStaticFiles)' == 'true'" Include="$(MonoObjDir)out\lib\libmono-ee-interp.a"> $(RuntimeBinDir)libmono-ee-interp.a diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index c66362a55f395..841bff5f566cf 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -381,6 +381,47 @@ if(NOT DISABLE_SHARED_LIBS) add_library(monosgen-shared-dac SHARED "mini-windows-dlldac.c") set_target_properties(monosgen-shared-dac PROPERTIES OUTPUT_NAME ${MONO_SHARED_LIB_NAME}-dac) endif() + + if(BUILD_DARWIN_FRAMEWORKS) + if(TARGET_DARWIN) + # In cmake, you cannot have list entries which contain a space or semicolon - those are considered + # record separators (i.e. a list of list(APPEND foo "a" "b;c" "d e") is a five entry list of values + # a, b, c, d and e. + # So, in order to treat the components lists as single list entries, swap out the ; character + # for a temporary replacement character, allowing the full lists to be treated as single entries + string(REPLACE ";" "*" mono-components-objects-nowhitespace "${mono-components-objects}") + string(REPLACE ";" "*" mono-components-stub-objects-nowhitespace "${mono-components-stub-objects}") + list(APPEND FrameworkConfig Mono.debug Mono.release) + list(APPEND ComponentsObjects "${mono-components-objects-nowhitespace}" "${mono-components-stub-objects-nowhitespace}") + foreach(frameworkconfig componentsobjects IN ZIP_LISTS FrameworkConfig ComponentsObjects) + if("${componentsobjects}" STREQUAL "") + #components list is empty, use stubs instead + set(componentsobjects "${mono-components-stub-objects-nowhitespace}") + endif() + add_library(${frameworkconfig} SHARED $) + target_compile_definitions(${frameworkconfig} PRIVATE -DMONO_DLL_EXPORT) + target_sources(${frameworkconfig} PRIVATE $) + target_link_libraries(${frameworkconfig} PRIVATE ${OS_LIBS} ${ICONV_LIB} ${LLVM_LIBS} ${ICU_LIBS}) + if(ICU_LDFLAGS) + set_property(TARGET ${frameworkconfig} APPEND_STRING PROPERTY LINK_FLAGS " ${ICU_LDFLAGS}") + endif() + if(STATIC_ICU) + set_property(TARGET ${frameworkconfig} APPEND_STRING PROPERTY LINKER_LANGUAGE CXX) + endif () + set_property(TARGET ${frameworkconfig} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0") + string(REPLACE "*" ";" componentsobjects-whitespace "${componentsobjects}") + target_sources(${frameworkconfig} PRIVATE "${componentsobjects-whitespace}") + set_target_properties(${frameworkconfig} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION C + MACOSX_FRAMEWORK_IDENTIFIER net.dot.mono-framework + ) + install(TARGETS ${frameworkconfig} + FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + endforeach() + endif() + endif() endif() find_package(Python3 COMPONENTS Interpreter) From 88732bc8ae912d79e9563d4f9fc05e23a4d44113 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Thu, 8 Jul 2021 21:47:50 +0200 Subject: [PATCH 02/72] [mono] Fix crash in AssemblyBuilder::GetModules (#55202) * The "modules" field may be null if called before the end of the AssemblyBuilder ctor (which may happen in an OnAssemblyLoad event handler invoked during the basic_init VM call). --- .../src/System/Reflection/Emit/AssemblyBuilder.Mono.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.Mono.cs index a6a932162ceb8..502d09d152da4 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.Mono.cs @@ -360,7 +360,12 @@ internal static Type MakeGenericType(Type gtd, Type[] typeArguments) => return null; } - public override Module[] GetModules(bool getResourceModules) => (Module[])modules.Clone(); + public override Module[] GetModules(bool getResourceModules) + { + if (modules == null) + return Array.Empty(); + return (Module[])modules.Clone(); + } public override AssemblyName GetName(bool copiedName) => AssemblyName.Create(_mono_assembly, null); From 31c2bed42a722735d5ed2abf4738acf6a0d0ee17 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:55:21 -0700 Subject: [PATCH 03/72] [main] Update dependencies from 10 repositories (#55074) * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210701.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21351.5 * Update dependencies from https://github.com/dotnet/arcade build 20210701.2 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21330.2 -> To Version 6.0.0-beta.21351.2 * Update dependencies from https://github.com/dotnet/xharness build 20210701.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21330.2 -> To Version 1.0.0-prerelease.21351.2 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210701.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21330.1 -> To Version 1.0.1-alpha.0.21351.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20210701.2 Microsoft.CodeAnalysis.NetAnalyzers From Version 6.0.0-rc1.21320.2 -> To Version 6.0.0-rc1.21351.2 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210702.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21352.5 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210702.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21330.1 -> To Version 1.0.1-alpha.0.21352.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20210702.1 Microsoft.CodeAnalysis.NetAnalyzers From Version 6.0.0-rc1.21320.2 -> To Version 6.0.0-rc1.21352.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210703.2 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21353.2 * Update dependencies from https://github.com/dotnet/runtime build 20210705.1 Microsoft.NETCore.DotNetHost , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , Microsoft.NET.Sdk.IL , System.Runtime.CompilerServices.Unsafe , System.Text.Json From Version 6.0.0-preview.7.21321.2 -> To Version 6.0.0-preview.7.21355.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210705.1 Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64 From Version 6.0.0-preview.7.21330.1 -> To Version 6.0.0-preview.7.21355.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210705.3 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21355.3 * Update dependencies from https://github.com/dotnet/arcade build 20210705.2 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21330.2 -> To Version 6.0.0-beta.21355.2 * Update dependencies from https://github.com/dotnet/llvm-project build 20210705.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 11.1.0-alpha.1.21328.1 -> To Version 11.1.0-alpha.1.21355.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20210706.1 System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Windows.Extensions.TestData From Version 6.0.0-beta.21314.1 -> To Version 6.0.0-beta.21356.1 * Update dependencies from https://github.com/mono/linker build 20210705.2 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21330.1 -> To Version 6.0.100-preview.6.21355.2 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210705.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21330.1 -> To Version 1.0.1-alpha.0.21355.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210706.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21330.1 -> To Version 6.0.0-preview.7.21356.1 * Bump Microsoft.DotNet.PackageValidation to 1.0.0-preview.7.21352.4 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210706.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21356.5 * Update dependencies from https://github.com/dotnet/xharness build 20210707.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21330.2 -> To Version 1.0.0-prerelease.21357.4 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20210706.1 Microsoft.CodeAnalysis.NetAnalyzers From Version 6.0.0-rc1.21320.2 -> To Version 6.0.0-rc1.21356.1 * Set AsArchive when payload is a zip file * Update dependencies from https://github.com/dotnet/arcade build 20210707.3 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21330.2 -> To Version 6.0.0-beta.21357.3 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210707.3 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21330.5 -> To Version 1.0.0-prerelease.21357.3 * Update dependencies from https://github.com/dotnet/llvm-project build 20210707.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 11.1.0-alpha.1.21328.1 -> To Version 11.1.0-alpha.1.21357.1 * Update dependencies from https://github.com/mono/linker build 20210707.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21330.1 -> To Version 6.0.100-preview.6.21357.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210708.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21330.1 -> To Version 6.0.0-preview.7.21358.1 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing Co-authored-by: Santiago Fernandez Madero --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 208 ++++++++++++++--------------- eng/Versions.props | 94 ++++++------- eng/common/SetupNugetSources.ps1 | 6 + eng/common/SetupNugetSources.sh | 24 ++++ global.json | 10 +- src/libraries/sendtohelixhelp.proj | 3 +- 7 files changed, 189 insertions(+), 158 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9c120588ffdef..8dd812c7094b4 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21330.2", + "version": "1.0.0-prerelease.21357.4", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 410d96a54a378..2389066e4b33e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,223 +8,223 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - f5349765b7af1970c5b25cce4ed278544907cbe0 + 5c9145289bd4d4e14b18a544dda60a185f66f688 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - ff3e7d23139c30feefe36d3d4e8d41a06160f254 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://github.com/dotnet/arcade - ff3e7d23139c30feefe36d3d4e8d41a06160f254 + 286d98094b830b8dad769542b2669cb1b75f7097 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d - + https://github.com/dotnet/llvm-project - 51c322893cff67a67e503d00e9c328d9d40b6a06 + a05e5e9fb80f9bb6fd9100775dfe55be6f84729d https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 - + https://github.com/mono/linker - f574448d16af45f7ac2c4b89d71dea73dec86726 + 35a1c74d6a0dbd115bf079dc986cea59cdb01430 - + https://github.com/dotnet/xharness - 6d17e5ba4709de02f2e5c62a308f8518253cb002 + c6d444eaf7e95339589ceef371cbef0a90a4add5 - + https://github.com/dotnet/xharness - 6d17e5ba4709de02f2e5c62a308f8518253cb002 + c6d444eaf7e95339589ceef371cbef0a90a4add5 - + https://github.com/dotnet/arcade - 26345756f99087811b1fe4d02ff213eb172ec506 + 286d98094b830b8dad769542b2669cb1b75f7097 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f + a89f052e97fec59a2d0148c08d3b4801567ec200 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f + a89f052e97fec59a2d0148c08d3b4801567ec200 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f + a89f052e97fec59a2d0148c08d3b4801567ec200 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f + a89f052e97fec59a2d0148c08d3b4801567ec200 - + https://github.com/dotnet/hotreload-utils - a8e0dc88077495e43d7820f631815fa95ce92f8a + 3960ef9a8980181e840b5c1d64ed0b234711e850 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 - + https://github.com/dotnet/roslyn-analyzers - fcddb771f42866f9521f23f093b1f30e129018bb + 77c6f0725c26442023c8eee2b143b899cb3f4eb7 diff --git a/eng/Versions.props b/eng/Versions.props index ba4255d77e5c3..8debb7be68dd1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,30 +50,30 @@ 3.10.0 3.10.0 - 6.0.0-rc1.21324.1 + 6.0.0-rc1.21356.1 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 2.5.1-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 2.5.1-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 6.0.0-preview.1.102 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21355.1 3.1.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 5.0.0 4.3.0 @@ -107,27 +107,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21355.1 4.5.4 4.5.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -151,9 +151,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21330.2 - 1.0.0-prerelease.21330.2 - 1.0.1-alpha.0.21330.1 + 1.0.0-prerelease.21357.4 + 1.0.0-prerelease.21357.4 + 1.0.1-alpha.0.21355.1 2.4.1 2.4.2 1.3.0 @@ -164,23 +164,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21330.1 + 6.0.100-preview.6.21357.1 $(MicrosoftNETILLinkTasksVersion) 6.0.0-preview.7.21328.1 6.0.0-preview.7.21357.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 - 6.0.0-preview.7.21330.1 + 6.0.0-preview.7.21355.1 $(MicrosoftNETWorkloadEmscriptenManifest60100) diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index a0b5fc37f4388..18823840b1127 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -158,4 +158,10 @@ if ($dotnet5Source -ne $null) { AddPackageSource -Sources $sources -SourceName "dotnet5-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password } +$dotnet6Source = $sources.SelectSingleNode("add[@key='dotnet6']") +if ($dotnet6Source -ne $null) { + AddPackageSource -Sources $sources -SourceName "dotnet6-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2" -Creds $creds -Username $userName -Password $Password + AddPackageSource -Sources $sources -SourceName "dotnet6-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password +} + $doc.Save($filename) diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index 2734601c13c4b..ad3fb74fd2cc8 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -129,6 +129,30 @@ if [ "$?" == "0" ]; then PackageSources+=('dotnet5-internal-transport') fi +# Ensure dotnet6-internal and dotnet6-internal-transport are in the packageSources if the public dotnet6 feeds are present +grep -i "" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet6-internal') + + grep -i "" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding dotnet6-internal-transport to the packageSources." + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet6-internal-transport') +fi + # I want things split line by line PrevIFS=$IFS IFS=$'\n' diff --git a/global.json b/global.json index ca21d08dc3bb1..99807111962c2 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21330.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21357.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21330.2", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21330.2", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21357.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21355.1" } } diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index d6d9a78c22e60..b5f603cee4f31 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -318,7 +318,8 @@ + Condition="'$(IncludeHelixCorrelationPayload)' == 'true' and '$(TargetOS)' != 'Browser'" + AsArchive="$(HelixCorrelationPayload.EndsWith('.zip'))" /> From d9f1ade5f0ec01c6e65b01f1e6d2280d959dfd9a Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Thu, 8 Jul 2021 13:15:12 -0700 Subject: [PATCH 04/72] fix read abort handling and revert CanRead/CanWrite to previous behavior (#55341) Co-authored-by: Geoffrey Kizer --- .../Quic/Implementations/Mock/MockStream.cs | 29 ++++++++++++--- .../Implementations/MsQuic/MsQuicStream.cs | 27 ++++++++++++-- .../tests/FunctionalTests/QuicStreamTests.cs | 37 +++++++++++++++---- .../tests/FunctionalTests/QuicTestBase.cs | 9 +++++ 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs index bd814f690d952..1d09b6331746b 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs @@ -70,7 +70,7 @@ internal override async ValueTask ReadAsync(Memory buffer, Cancellati int bytesRead = await streamBuffer.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { - long errorCode = _isInitiator ? _streamState._inboundErrorCode : _streamState._outboundErrorCode; + long errorCode = _isInitiator ? _streamState._inboundReadErrorCode : _streamState._outboundReadErrorCode; if (errorCode != 0) { throw new QuicStreamAbortedException(errorCode); @@ -121,6 +121,12 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool e throw new NotSupportedException(); } + long errorCode = _isInitiator ? _streamState._inboundWriteErrorCode : _streamState._outboundWriteErrorCode; + if (errorCode != 0) + { + throw new QuicStreamAbortedException(errorCode); + } + using var registration = cancellationToken.UnsafeRegister(static s => { var stream = (MockStream)s!; @@ -171,18 +177,27 @@ internal override Task FlushAsync(CancellationToken cancellationToken) internal override void AbortRead(long errorCode) { - throw new NotImplementedException(); + if (_isInitiator) + { + _streamState._outboundWriteErrorCode = errorCode; + } + else + { + _streamState._inboundWriteErrorCode = errorCode; + } + + ReadStreamBuffer?.AbortRead(); } internal override void AbortWrite(long errorCode) { if (_isInitiator) { - _streamState._outboundErrorCode = errorCode; + _streamState._outboundReadErrorCode = errorCode; } else { - _streamState._inboundErrorCode = errorCode; + _streamState._inboundReadErrorCode = errorCode; } WriteStreamBuffer?.EndWrite(); @@ -255,8 +270,10 @@ internal sealed class StreamState public readonly long _streamId; public StreamBuffer _outboundStreamBuffer; public StreamBuffer? _inboundStreamBuffer; - public long _outboundErrorCode; - public long _inboundErrorCode; + public long _outboundReadErrorCode; + public long _inboundReadErrorCode; + public long _outboundWriteErrorCode; + public long _inboundWriteErrorCode; private const int InitialBufferSize = #if DEBUG diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 8260dfdd68fa7..fed61e11e5695 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -20,6 +20,9 @@ internal sealed class MsQuicStream : QuicStreamProvider private readonly State _state = new State(); + private readonly bool _canRead; + private readonly bool _canWrite; + // Backing for StreamId private long _streamId = -1; @@ -80,8 +83,10 @@ public void Cleanup() internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { _state.Handle = streamHandle; + _canRead = true; + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _started = true; - if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + if (!_canWrite) { _state.SendState = SendState.Closed; } @@ -122,8 +127,11 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F { Debug.Assert(connectionState.Handle != null); + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); + _canWrite = true; + _state.StateGCHandle = GCHandle.Alloc(_state); - if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + if (!_canRead) { _state.ReadState = ReadState.Closed; } @@ -167,9 +175,9 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F } } - internal override bool CanRead => _disposed == 0 && _state.ReadState < ReadState.Aborted; + internal override bool CanRead => _disposed == 0 && _canRead; - internal override bool CanWrite => _disposed == 0 && _state.SendState < SendState.Aborted; + internal override bool CanWrite => _disposed == 0 && _canWrite; internal override long StreamId { @@ -242,6 +250,11 @@ private async ValueTask HandleWriteStartState(Can } else if ( _state.SendState == SendState.Aborted) { + if (_state.SendErrorCode != -1) + { + throw new QuicStreamAbortedException(_state.SendErrorCode); + } + throw new OperationCanceledException(cancellationToken); } @@ -292,6 +305,12 @@ private async ValueTask HandleWriteStartState(Can if (_state.SendState == SendState.Aborted) { cancellationToken.ThrowIfCancellationRequested(); + + if (_state.SendErrorCode != -1) + { + throw new QuicStreamAbortedException(_state.SendErrorCode); + } + throw new OperationCanceledException(SR.net_quic_sending_aborted); } else if (_state.SendState == SendState.ConnectionClosed) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index dacd04a448cbe..7b40903651824 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -437,17 +437,15 @@ await Task.Run(async () => } [Fact] - public async Task StreamAbortedWithoutWriting_ReadThrows() + public async Task WriteAbortedWithoutWriting_ReadThrows() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => { await using QuicStream stream = connection.OpenUnidirectionalStream(); stream.AbortWrite(expectedErrorCode); - - await stream.ShutdownCompleted(); }, serverFunction: async connection => { @@ -458,7 +456,32 @@ await RunClientServer( QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadAll(stream, buffer)); Assert.Equal(expectedErrorCode, ex.ErrorCode); - await stream.ShutdownCompleted(); + // We should still return true from CanRead, even though the read has been aborted. + Assert.True(stream.CanRead); + } + ); + } + + [Fact] + public async Task ReadAbortedWithoutReading_WriteThrows() + { + const long expectedErrorCode = 1234; + + await RunClientServer( + clientFunction: async connection => + { + await using QuicStream stream = connection.OpenBidirectionalStream(); + stream.AbortRead(expectedErrorCode); + }, + serverFunction: async connection => + { + await using QuicStream stream = await connection.AcceptStreamAsync(); + + QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => WriteForever(stream)); + Assert.Equal(expectedErrorCode, ex.ErrorCode); + + // We should still return true from CanWrite, even though the write has been aborted. + Assert.True(stream.CanWrite); } ); } @@ -466,7 +489,7 @@ await RunClientServer( [Fact] public async Task WritePreCanceled_Throws() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => @@ -502,7 +525,7 @@ await RunClientServer( [Fact] public async Task WriteCanceled_NextWriteThrows() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index ee7501868beba..c19242342f275 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -130,6 +130,15 @@ internal static async Task ReadAll(QuicStream stream, byte[] buffer) return bytesRead; } + internal static async Task WriteForever(QuicStream stream) + { + Memory buffer = new byte[] { 123 }; + while (true) + { + await stream.WriteAsync(buffer); + } + } + internal static void AssertArrayEqual(byte[] expected, byte[] actual) { for (int i = 0; i < expected.Length; ++i) From 7ae4d1acbc73b5d8e3f79797e5e73d5a546e8470 Mon Sep 17 00:00:00 2001 From: imhameed Date: Thu, 8 Jul 2021 13:28:20 -0700 Subject: [PATCH 05/72] [mono] Make Mono CMake Windows build work without mono.proj (#54855) --- src/mono/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 52a82116bc424..4c25d97b7a5d9 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -16,7 +16,7 @@ if(NOT MONO_LIB_NAME) endif() if(NOT MONO_SHARED_LIB_NAME) - set(MONO_SHARED_LIB_NAME "$(MONO_LIB_NAME)") + set(MONO_SHARED_LIB_NAME "${MONO_LIB_NAME}") endif() include(GNUInstallDirs) @@ -712,8 +712,15 @@ endif() ###################################### # EXTRACT VERSION ###################################### -if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/_version.h") - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "static char sccsid[] __attribute__((used)) = \"@(#)Version 42.42.42.42424 @Commit: AAA\";\n") +if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/_version.h") + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "#undef VER_PRODUCTVERSION_STR\n#define VER_PRODUCTVERSION_STR \"42.42.42.42424\"\n") + else() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "static char sccsid[] __attribute__((used)) = \"@(#)Version 42.42.42.42424 @Commit: AAA\";\n") + endif() +endif() +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows" AND NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/NativeVersion.rc") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/NativeVersion.rc" "\n") endif() if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/runtime_version.h") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/runtime_version.h" From 24329262b88fd29951a7b561169c12e82c668b19 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 8 Jul 2021 15:29:10 -0500 Subject: [PATCH 06/72] Fix NullReferenceException in background thread emitting IL in DependencyInjection (#55340) * Fix NullReferenceException in background thread emitting IL in DependencyInjection When ILEmitResolverBuilder is getting created, it grabs the "Root" scope off of the ServiceProvider. However, the Root scope isn't set on ServiceProvider yet. So later when it tries to get used, it null refs. But this exception gets caught and eaten since it happens on a background thread. The fix is to set Root before creating the ServiceProviderEngine. * Add a debug assert that we shouldn't get exceptions from the background compilation thread --- .../src/ServiceLookup/DynamicServiceProviderEngine.cs | 3 +++ .../src/ServiceProvider.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index ee124f31be8ef..0237e8ad096b7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Threading; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup @@ -37,6 +38,8 @@ public override Func RealizeService(ServiceC catch (Exception ex) { DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex); + + Debug.Fail($"We should never get exceptions from the background compilation.{Environment.NewLine}{ex}"); } }, null); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index b93a11d956619..afe118acf7909 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -32,11 +32,12 @@ public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDispo internal ServiceProvider(IEnumerable serviceDescriptors, ServiceProviderOptions options) { + // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root + Root = new ServiceProviderEngineScope(this, isRootScope: true); _engine = GetEngine(); _createServiceAccessor = CreateServiceAccessor; _realizedServices = new ConcurrentDictionary>(); - Root = new ServiceProviderEngineScope(this, isRootScope: true); CallSiteFactory = new CallSiteFactory(serviceDescriptors); // The list of built in services that aren't part of the list of service descriptors // keep this in sync with CallSiteFactory.IsService From f3b777546cceaeea7693b22a16bc08c797ec38bc Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 9 Jul 2021 00:14:21 +0300 Subject: [PATCH 07/72] Print indices of assertions instead of raw bitsets (#54928) * Add JITDUMPEXEC macro For use in contexts where some printing method should only be executed when "verbose" is true. * Add helpers for printing assertion indexes * Print assertion indices instead of raw bitsets To aid in understanding what assertions are being propagated and merged when reading the dumps. * Don't print VNs for the same assertion twice * Also correctly print VNs in CopyProp * Align "in"s with "out"s for final assertions * Don't print the assertion dataflow in usual dumps It can still be enabled if needed. --- src/coreclr/jit/assertionprop.cpp | 184 ++++++++++++++++++++++-------- src/coreclr/jit/compiler.h | 5 + src/coreclr/jit/copyprop.cpp | 4 +- src/coreclr/jit/jit.h | 6 + src/coreclr/jit/rangecheck.cpp | 15 +-- 5 files changed, 160 insertions(+), 54 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 981879142bf26..fb869c89fee53 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -558,7 +558,7 @@ void Compiler::optAssertionInit(bool isLocalProp) } #ifdef DEBUG -void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* =0 */) +void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* = 0 */) { if (curAssertion->op1.kind == O1K_EXACT_TYPE) { @@ -590,10 +590,6 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse printf("?assertion classification? "); } printf("Assertion: "); - if (!optLocalAssertionProp) - { - printf("(%d, %d) ", curAssertion->op1.vn, curAssertion->op2.vn); - } if (!optLocalAssertionProp) { @@ -778,12 +774,67 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse if (assertionIndex > 0) { - printf(" index=#%02u, mask=", assertionIndex); - printf("%s", BitVecOps::ToString(apTraits, BitVecOps::MakeSingleton(apTraits, assertionIndex - 1))); + printf(", index = "); + optPrintAssertionIndex(assertionIndex); } printf("\n"); } + +void Compiler::optPrintAssertionIndex(AssertionIndex index) +{ + if (index == NO_ASSERTION_INDEX) + { + printf("#NA"); + return; + } + + printf("#%02u", index); +} + +void Compiler::optPrintAssertionIndices(ASSERT_TP assertions) +{ + if (BitVecOps::IsEmpty(apTraits, assertions)) + { + optPrintAssertionIndex(NO_ASSERTION_INDEX); + return; + } + + BitVecOps::Iter iter(apTraits, assertions); + unsigned bitIndex = 0; + if (iter.NextElem(&bitIndex)) + { + optPrintAssertionIndex(static_cast(bitIndex + 1)); + while (iter.NextElem(&bitIndex)) + { + printf(" "); + optPrintAssertionIndex(static_cast(bitIndex + 1)); + } + } +} +#endif // DEBUG + +/* static */ +void Compiler::optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer /* = nullptr */) +{ +#ifdef DEBUG + Compiler* compiler = JitTls::GetCompiler(); + if (compiler->verbose) + { + printf(header); + compiler->optPrintAssertionIndices(assertions); + if (footer != nullptr) + { + printf(footer); + } + } #endif // DEBUG +} + +/* static */ +void Compiler::optDumpAssertionIndices(ASSERT_TP assertions, const char* footer /* = nullptr */) +{ + optDumpAssertionIndices("", assertions, footer); +} /****************************************************************************** * @@ -4511,7 +4562,7 @@ void Compiler::optImpliedByConstAssertion(AssertionDsc* constAssertion, ASSERT_T if (verbose) { AssertionDsc* firstAssertion = optGetAssertion(1); - printf("\nCompiler::optImpliedByConstAssertion: constAssertion #%02d , implies assertion #%02d", + printf("Compiler::optImpliedByConstAssertion: const assertion #%02d implies assertion #%02d\n", (constAssertion - firstAssertion) + 1, (impAssertion - firstAssertion) + 1); } #endif @@ -4721,8 +4772,12 @@ class AssertionPropFlowCallback // At the start of the merge function of the dataflow equations, initialize premerge state (to detect change.) void StartMerge(BasicBlock* block) { - JITDUMP("AssertionPropCallback::StartMerge: " FMT_BB " in -> %s\n", block->bbNum, - BitVecOps::ToString(apTraits, block->bbAssertionIn)); + if (VerboseDataflow()) + { + JITDUMP("StartMerge: " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n"); + } + BitVecOps::Assign(apTraits, preMergeOut, block->bbAssertionOut); BitVecOps::Assign(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum]); } @@ -4743,11 +4798,14 @@ class AssertionPropFlowCallback assert(predBlock->bbNext == block); BitVecOps::IntersectionD(apTraits, pAssertionOut, predBlock->bbAssertionOut); - JITDUMP("AssertionPropCallback::Merge : Duplicate flow, " FMT_BB " in -> %s, predBlock " FMT_BB - " out1 -> %s, out2 -> %s\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum, - BitVecOps::ToString(apTraits, mJumpDestOut[predBlock->bbNum]), - BitVecOps::ToString(apTraits, predBlock->bbAssertionOut)); + if (VerboseDataflow()) + { + JITDUMP("Merge : Duplicate flow, " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; "); + JITDUMP("pred " FMT_BB " ", predBlock->bbNum); + Compiler::optDumpAssertionIndices("out1 -> ", mJumpDestOut[predBlock->bbNum], "; "); + Compiler::optDumpAssertionIndices("out2 -> ", predBlock->bbAssertionOut, "\n"); + } } } else @@ -4755,9 +4813,14 @@ class AssertionPropFlowCallback pAssertionOut = predBlock->bbAssertionOut; } - JITDUMP("AssertionPropCallback::Merge : " FMT_BB " in -> %s, predBlock " FMT_BB " out -> %s\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum, - BitVecOps::ToString(apTraits, pAssertionOut)); + if (VerboseDataflow()) + { + JITDUMP("Merge : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; "); + JITDUMP("pred " FMT_BB " ", predBlock->bbNum); + Compiler::optDumpAssertionIndices("out -> ", pAssertionOut, "\n"); + } + BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, pAssertionOut); } @@ -4781,8 +4844,11 @@ class AssertionPropFlowCallback // At the end of the merge store results of the dataflow equations, in a postmerge state. bool EndMerge(BasicBlock* block) { - JITDUMP("AssertionPropCallback::EndMerge : " FMT_BB " in -> %s\n\n", block->bbNum, - BitVecOps::ToString(apTraits, block->bbAssertionIn)); + if (VerboseDataflow()) + { + JITDUMP("EndMerge : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n\n"); + } BitVecOps::DataFlowD(apTraits, block->bbAssertionOut, block->bbAssertionGen, block->bbAssertionIn); BitVecOps::DataFlowD(apTraits, mJumpDestOut[block->bbNum], mJumpDestGen[block->bbNum], block->bbAssertionIn); @@ -4790,24 +4856,35 @@ class AssertionPropFlowCallback bool changed = (!BitVecOps::Equal(apTraits, preMergeOut, block->bbAssertionOut) || !BitVecOps::Equal(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum])); - if (changed) - { - JITDUMP("AssertionPropCallback::Changed : " FMT_BB " before out -> %s; after out -> %s;\n" - "\t\tjumpDest before out -> %s; jumpDest after out -> %s;\n\n", - block->bbNum, BitVecOps::ToString(apTraits, preMergeOut), - BitVecOps::ToString(apTraits, block->bbAssertionOut), - BitVecOps::ToString(apTraits, preMergeJumpDestOut), - BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum])); - } - else + if (VerboseDataflow()) { - JITDUMP("AssertionPropCallback::Unchanged : " FMT_BB " out -> %s; \t\tjumpDest out -> %s\n\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionOut), - BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum])); + if (changed) + { + JITDUMP("Changed : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("before out -> ", preMergeOut, "; "); + Compiler::optDumpAssertionIndices("after out -> ", block->bbAssertionOut, ";\n "); + Compiler::optDumpAssertionIndices("jumpDest before out -> ", preMergeJumpDestOut, "; "); + Compiler::optDumpAssertionIndices("jumpDest after out -> ", mJumpDestOut[block->bbNum], ";\n\n"); + } + else + { + JITDUMP("Unchanged : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("out -> ", block->bbAssertionOut, "; "); + Compiler::optDumpAssertionIndices("jumpDest out -> ", mJumpDestOut[block->bbNum], "\n\n"); + } } return changed; } + + // Can be enabled to get detailed debug output about dataflow for assertions. + bool VerboseDataflow() + { +#if 0 + return VERBOSE; +#endif + return false; + } }; /***************************************************************************** @@ -4894,16 +4971,28 @@ ASSERT_TP* Compiler::optComputeAssertionGen() #ifdef DEBUG if (verbose) { - printf(FMT_BB " valueGen = %s", block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionGen)); + if (block == fgFirstBB) + { + printf("\n"); + } + + printf(FMT_BB " valueGen = ", block->bbNum); + optPrintAssertionIndices(block->bbAssertionGen); if (block->bbJumpKind == BBJ_COND) { - printf(" => " FMT_BB " valueGen = %s,", block->bbJumpDest->bbNum, - BitVecOps::ToString(apTraits, jumpDestGen[block->bbNum])); + printf(" => " FMT_BB " valueGen = ", block->bbJumpDest->bbNum); + optPrintAssertionIndices(jumpDestGen[block->bbNum]); } printf("\n"); + + if (block == fgLastBB) + { + printf("\n"); + } } #endif } + return jumpDestGen; } @@ -5408,6 +5497,10 @@ void Compiler::optAssertionPropMain() // Modified dataflow algorithm for available expressions. DataFlow flow(this); AssertionPropFlowCallback ap(this, bbJtrueAssertionOut, jumpDestGen); + if (ap.VerboseDataflow()) + { + JITDUMP("AssertionPropFlowCallback:\n\n") + } flow.ForwardAnalysis(ap); for (BasicBlock* const block : Blocks()) @@ -5419,16 +5512,15 @@ void Compiler::optAssertionPropMain() #ifdef DEBUG if (verbose) { - printf("\n"); for (BasicBlock* const block : Blocks()) { - printf("\n" FMT_BB, block->bbNum); - printf(" valueIn = %s", BitVecOps::ToString(apTraits, block->bbAssertionIn)); - printf(" valueOut = %s", BitVecOps::ToString(apTraits, block->bbAssertionOut)); + printf(FMT_BB ":\n", block->bbNum); + optDumpAssertionIndices(" in = ", block->bbAssertionIn, "\n"); + optDumpAssertionIndices(" out = ", block->bbAssertionOut, "\n"); if (block->bbJumpKind == BBJ_COND) { - printf(" => " FMT_BB, block->bbJumpDest->bbNum); - printf(" valueOut= %s", BitVecOps::ToString(apTraits, bbJtrueAssertionOut[block->bbNum])); + printf(" " FMT_BB " = ", block->bbJumpDest->bbNum); + optDumpAssertionIndices(bbJtrueAssertionOut[block->bbNum], "\n"); } } printf("\n"); @@ -5473,9 +5565,11 @@ void Compiler::optAssertionPropMain() // and thus we must morph, set order, re-link for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) { - JITDUMP("Propagating %s assertions for " FMT_BB ", stmt " FMT_STMT ", tree [%06d], tree -> %d\n", - BitVecOps::ToString(apTraits, assertions), block->bbNum, stmt->GetID(), dspTreeID(tree), - tree->GetAssertionInfo().GetAssertionIndex()); + optDumpAssertionIndices("Propagating ", assertions, " "); + JITDUMP("for " FMT_BB ", stmt " FMT_STMT ", tree [%06d]", block->bbNum, stmt->GetID(), dspTreeID(tree)); + JITDUMP(", tree -> "); + JITDUMPEXEC(optPrintAssertionIndex(tree->GetAssertionInfo().GetAssertionIndex())); + JITDUMP("\n"); GenTree* newTree = optAssertionProp(assertions, tree, stmt, block); if (newTree) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 356fe6498d166..639b67bb15c94 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7491,9 +7491,14 @@ class Compiler #ifdef DEBUG void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex = 0); + void optPrintAssertionIndex(AssertionIndex index); + void optPrintAssertionIndices(ASSERT_TP assertions); void optDebugCheckAssertion(AssertionDsc* assertion); void optDebugCheckAssertions(AssertionIndex AssertionIndex); #endif + static void optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer = nullptr); + static void optDumpAssertionIndices(ASSERT_TP assertions, const char* footer = nullptr); + void optAddCopies(); #endif // ASSERTION_PROP diff --git a/src/coreclr/jit/copyprop.cpp b/src/coreclr/jit/copyprop.cpp index c0d72123e1a11..625e2eee5e8b0 100644 --- a/src/coreclr/jit/copyprop.cpp +++ b/src/coreclr/jit/copyprop.cpp @@ -270,9 +270,9 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc { JITDUMP("VN based copy assertion for "); printTreeID(tree); - printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative)); + printf(" V%02d " FMT_VN " by ", lclNum, tree->GetVN(VNK_Conservative)); printTreeID(op); - printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative)); + printf(" V%02d " FMT_VN ".\n", newLclNum, op->GetVN(VNK_Conservative)); gtDispTree(tree, nullptr, nullptr, true); } #endif diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 2dd39559c282e..fb39fda019c45 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -487,6 +487,11 @@ const bool dspGCtbls = true; if (JitTls::GetCompiler()->verbose) \ logf(__VA_ARGS__); \ } +#define JITDUMPEXEC(x) \ + { \ + if (JitTls::GetCompiler()->verbose) \ + x; \ + } #define JITLOG(x) \ { \ JitLogEE x; \ @@ -521,6 +526,7 @@ const bool dspGCtbls = true; #define VERBOSE JitTls::GetCompiler()->verbose #else // !DEBUG #define JITDUMP(...) +#define JITDUMPEXEC(x) #define JITLOG(x) #define JITLOG_THIS(t, x) #define DBEXEC(flg, expr) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 5c95b91883c52..a37f11cedaeee 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -865,16 +865,16 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE if (pred->bbFallsThrough() && pred->bbNext == block) { assertions = pred->bbAssertionOut; - JITDUMP("Merge assertions from pred " FMT_BB " edge: %s\n", pred->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, assertions)); + JITDUMP("Merge assertions from pred " FMT_BB " edge: ", pred->bbNum); + Compiler::optDumpAssertionIndices(assertions, "\n"); } else if ((pred->bbJumpKind == BBJ_COND || pred->bbJumpKind == BBJ_ALWAYS) && pred->bbJumpDest == block) { if (m_pCompiler->bbJtrueAssertionOut != nullptr) { assertions = m_pCompiler->bbJtrueAssertionOut[pred->bbNum]; - JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: %s\n", pred->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, assertions)); + JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: ", pred->bbNum); + Compiler::optDumpAssertionIndices(assertions, "\n"); } } } @@ -1012,9 +1012,10 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block, Range range = GetRange(ssaDef->GetBlock(), ssaDef->GetAssignment()->gtGetOp2(), monIncreasing DEBUGARG(indent)); if (!BitVecOps::MayBeUninit(block->bbAssertionIn) && (m_pCompiler->GetAssertionCount() > 0)) { - JITDUMP("Merge assertions from " FMT_BB ":%s for assignment about [%06d]\n", block->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, block->bbAssertionIn), - Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1())); + JITDUMP("Merge assertions from " FMT_BB ": ", block->bbNum); + Compiler::optDumpAssertionIndices(block->bbAssertionIn, " "); + JITDUMP("for assignment about [%06d]\n", Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1())) + MergeEdgeAssertions(ssaDef->GetAssignment()->gtGetOp1()->AsLclVarCommon(), block->bbAssertionIn, &range); JITDUMP("done merging\n"); } From fdbca22b23727ec640666d385b3c41ffe4b7b914 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 9 Jul 2021 00:15:20 +0300 Subject: [PATCH 08/72] Don't use GT_ARR_ELEM as a location/value (#54780) * Don't use GT_ARR_ELEM as a location It represents an address. No diffs. * Clarify the purpose of GenTreeArrElem --- src/coreclr/jit/assertionprop.cpp | 7 ------- src/coreclr/jit/gcinfo.cpp | 1 - src/coreclr/jit/gentree.h | 8 ++++---- src/coreclr/jit/morph.cpp | 1 - 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index fb869c89fee53..ffad32a61b200 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -1248,13 +1248,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, toType = op2->gtType; goto SUBRANGE_COMMON; - case GT_ARR_ELEM: - - /* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */ - - toType = op2->gtType; - goto SUBRANGE_COMMON; - case GT_LCL_FLD: /* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */ diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp index 1b4d50fe875c5..cc8e1cd585f84 100644 --- a/src/coreclr/jit/gcinfo.cpp +++ b/src/coreclr/jit/gcinfo.cpp @@ -272,7 +272,6 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree case GT_LEA: return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base()); - case GT_ARR_ELEM: /* Definitely in the managed heap */ case GT_CLS_VAR: return WBF_BarrierUnchecked; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b043956468848..c12e46f02bdab 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5366,9 +5366,9 @@ struct GenTreeBoundsChk : public GenTree } }; -// gtArrElem -- general array element (GT_ARR_ELEM), for non "SZ_ARRAYS" -// -- multidimensional arrays, or 1-d arrays with non-zero lower bounds. - +// GenTreeArrElem - bounds checked address (byref) of a general array element, +// for multidimensional arrays, or 1-d arrays with non-zero lower bounds. +// struct GenTreeArrElem : public GenTree { GenTree* gtArrObj; @@ -5384,7 +5384,7 @@ struct GenTreeArrElem : public GenTree // This has caused VSW 571394. var_types gtArrElemType; // The array element type - // Requires that "inds" is a pointer to an array of "rank" GenTreePtrs for the indices. + // Requires that "inds" is a pointer to an array of "rank" nodes for the indices. GenTreeArrElem( var_types type, GenTree* arr, unsigned char rank, unsigned char elemSize, var_types elemType, GenTree** inds) : GenTree(GT_ARR_ELEM, type), gtArrObj(arr), gtArrRank(rank), gtArrElemSize(elemSize), gtArrElemType(elemType) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2b40d8548c68f..9f734adaa71c9 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -639,7 +639,6 @@ GenTree* Compiler::fgMorphCast(GenTree* tree) case GT_IND: case GT_CLS_VAR: case GT_LCL_FLD: - case GT_ARR_ELEM: oper->gtType = dstType; // We're changing the type here so we need to update the VN; // in other cases we discard the cast without modifying oper From 9dd6d2e8f626eaf1fb3020f8fae50d701a9d658f Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 8 Jul 2021 16:43:11 -0500 Subject: [PATCH 09/72] Remove pack references (#55359) Remove duplicate pack references --- .../WorkloadManifest.json.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index e6a7ff1ee2a02..b4ddda95fe037 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -9,10 +9,7 @@ "packs": [ "Microsoft.NET.Runtime.WebAssembly.Sdk", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", - "Microsoft.NETCore.App.Runtime.AOT.Cross.browser-wasm", - "Microsoft.NET.Runtime.Emscripten.Node", - "Microsoft.NET.Runtime.Emscripten.Python", - "Microsoft.NET.Runtime.Emscripten.Sdk" + "Microsoft.NETCore.App.Runtime.AOT.Cross.browser-wasm" ], "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "linux-x64", "osx-x64", "osx-arm64" ] From b2a5499230f005108b882ac8bd76b3868d2573e4 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 8 Jul 2021 15:38:40 -0700 Subject: [PATCH 10/72] Loop alignment: Handle blocks added in loop as part of split edges of LSRA (#55047) * Handle blocks added in loop as part of split edges of LSRA If there are new blocks added by LSRA and modifies the flow of blocks that are in loop, then make sure that we do not align such loops if they intersect with last aligned loop. * Retain LOOP_ALIGN flag of loops whose start are same * jit format * review feedback --- src/coreclr/jit/emit.cpp | 114 ++++++++++++++++++++++++++++----------- src/coreclr/jit/emit.h | 12 ++--- 2 files changed, 90 insertions(+), 36 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 352d1b2acfcd7..913951568fd1b 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1023,9 +1023,9 @@ void emitter::emitBegFN(bool hasFramePtr emitIGbuffSize = 0; #if FEATURE_LOOP_ALIGN - emitLastAlignedIgNum = 0; - emitLastInnerLoopStartIgNum = 0; - emitLastInnerLoopEndIgNum = 0; + emitLastAlignedIgNum = 0; + emitLastLoopStart = 0; + emitLastLoopEnd = 0; #endif /* Record stack frame info (the temp size is just an estimate) */ @@ -4820,58 +4820,112 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG // if currIG has back-edge to dstIG. // // Notes: -// If the current loop encloses a loop that is already marked as align, then remove -// the alignment flag present on IG before dstIG. +// Despite we align only inner most loop, we might see intersected loops because of control flow +// re-arrangement like adding a split edge in LSRA. +// +// If there is an intersection of current loop with last loop that is already marked as align, +// then *do not align* one of the loop that completely encloses the other one. Or if they both intersect, +// then *do not align* either of them because since the flow is complicated enough that aligning one of them +// will not improve the performance. // void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock) { - insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie; + insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie; + bool alignCurrentLoop = true; + bool alignLastLoop = true; // With (dstIG != nullptr), ensure that only back edges are tracked. // If there is forward jump, dstIG is not yet generated. // // We don't rely on (block->bbJumpDest->bbNum <= block->bbNum) because the basic // block numbering is not guaranteed to be sequential. - if ((dstIG != nullptr) && (dstIG->igNum <= emitCurIG->igNum)) { unsigned currLoopStart = dstIG->igNum; unsigned currLoopEnd = emitCurIG->igNum; // Only mark back-edge if current loop starts after the last inner loop ended. - if (emitLastInnerLoopEndIgNum < currLoopStart) + if (emitLastLoopEnd < currLoopStart) { emitCurIG->igLoopBackEdge = dstIG; JITDUMP("** IG%02u jumps back to IG%02u forming a loop.\n", currLoopEnd, currLoopStart); - emitLastInnerLoopStartIgNum = currLoopStart; - emitLastInnerLoopEndIgNum = currLoopEnd; + emitLastLoopStart = currLoopStart; + emitLastLoopEnd = currLoopEnd; + } + else if (currLoopStart == emitLastLoopStart) + { + // Note: If current and last loop starts at same point, + // retain the alignment flag of the smaller loop. + // | + // .---->|<----. + // last | | | + // loop | | | current + // .---->| | loop + // | | + // |-----. + // + } + else if ((currLoopStart < emitLastLoopStart) && (emitLastLoopEnd < currLoopEnd)) + { + // if current loop completely encloses last loop, + // then current loop should not be aligned. + alignCurrentLoop = false; + } + else if ((emitLastLoopStart < currLoopStart) && (currLoopEnd < emitLastLoopEnd)) + { + // if last loop completely encloses current loop, + // then last loop should not be aligned. + alignLastLoop = false; + } + else + { + // The loops intersect and should not align either of the loops + alignLastLoop = false; + alignCurrentLoop = false; } - // Otherwise, mark the dstIG->prevIG as no alignment needed. - // - // Note: If current loop's back-edge target is same as emitLastInnerLoopStartIgNum, - // retain the alignment flag of dstIG->prevIG so the loop - // (emitLastInnerLoopStartIgNum ~ emitLastInnerLoopEndIgNum) is still aligned. - else if (emitLastInnerLoopStartIgNum != currLoopStart) - { - // Find the IG before dstIG... - instrDescAlign* alignInstr = emitAlignList; - while ((alignInstr != nullptr) && (alignInstr->idaIG->igNext != dstIG)) - { - alignInstr = alignInstr->idaNext; - } - // ...and clear the IGF_LOOP_ALIGN flag - if (alignInstr != nullptr) + if (!alignLastLoop || !alignCurrentLoop) + { + instrDescAlign* alignInstr = emitAlignList; + bool markedLastLoop = alignLastLoop; + bool markedCurrLoop = alignCurrentLoop; + while ((alignInstr != nullptr)) { - assert(alignInstr->idaIG->igNext == dstIG); - alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; + // Find the IG before current loop and clear the IGF_LOOP_ALIGN flag + if (!alignCurrentLoop && (alignInstr->idaIG->igNext == dstIG)) + { + assert(!markedCurrLoop); + alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; + markedCurrLoop = true; + JITDUMP("** Skip alignment for current loop IG%02u ~ IG%02u because it encloses an aligned loop " + "IG%02u ~ IG%02u.\n", + currLoopStart, currLoopEnd, emitLastLoopStart, emitLastLoopEnd); + } + + // Find the IG before the last loop and clear the IGF_LOOP_ALIGN flag + if (!alignLastLoop && (alignInstr->idaIG->igNext != nullptr) && + (alignInstr->idaIG->igNext->igNum == emitLastLoopStart)) + { + assert(!markedLastLoop); + assert(alignInstr->idaIG->isLoopAlign()); + alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; + markedLastLoop = true; + JITDUMP("** Skip alignment for aligned loop IG%02u ~ IG%02u because it encloses the current loop " + "IG%02u ~ IG%02u.\n", + emitLastLoopStart, emitLastLoopEnd, currLoopStart, currLoopEnd); + } + + if (markedLastLoop && markedCurrLoop) + { + break; + } + + alignInstr = alignInstr->idaNext; } - JITDUMP( - "** Skip alignment for loop IG%02u ~ IG%02u, because it encloses an aligned loop IG%02u ~ IG%02u.\n", - currLoopStart, currLoopEnd, emitLastInnerLoopStartIgNum, emitLastInnerLoopEndIgNum); + assert(markedLastLoop && markedCurrLoop); } } } diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 666201fc98bf0..2748acf463766 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1762,12 +1762,12 @@ class emitter void emitJumpDistBind(); // Bind all the local jumps in method #if FEATURE_LOOP_ALIGN - instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG - unsigned emitLastInnerLoopStartIgNum; // Start IG of last inner loop - unsigned emitLastInnerLoopEndIgNum; // End IG of last inner loop - unsigned emitLastAlignedIgNum; // last IG that has align instruction - instrDescAlign* emitAlignList; // list of local align instructions in method - instrDescAlign* emitAlignLast; // last align instruction in method + instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG + unsigned emitLastLoopStart; // Start IG of last inner loop + unsigned emitLastLoopEnd; // End IG of last inner loop + unsigned emitLastAlignedIgNum; // last IG that has align instruction + instrDescAlign* emitAlignList; // list of local align instructions in method + instrDescAlign* emitAlignLast; // last align instruction in method unsigned getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG_ARG(bool isAlignAdjusted)); // Get the smallest loop size void emitLoopAlignment(); From 03cb9460334e1b145c33771a77462df3d1e99db4 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 8 Jul 2021 23:17:47 -0400 Subject: [PATCH 11/72] Move DiagnosticsHandler.IsGloballyEnabled back to a method (#55351) Intent is to make linker substitutions happy and try to unblock dotnet/sdk#18801 --- .../System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs | 4 ++-- .../System.Net.Http/src/System/Net/Http/HttpClientHandler.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index f3de5028d911c..21b161f9fbb4a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -31,10 +31,10 @@ internal static bool IsEnabled() { // check if there is a parent Activity (and propagation is not suppressed) // or if someone listens to HttpHandlerDiagnosticListener - return IsGloballyEnabled && (Activity.Current != null || s_diagnosticListener.IsEnabled()); + return IsGloballyEnabled() && (Activity.Current != null || s_diagnosticListener.IsEnabled()); } - internal static bool IsGloballyEnabled => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; + internal static bool IsGloballyEnabled() => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; // SendAsyncCore returns already completed ValueTask for when async: false is passed. // Internally, it calls the synchronous Send method of the base class. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 89cb508d220e4..2e3289643cfbe 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -28,7 +28,7 @@ public partial class HttpClientHandler : HttpMessageHandler public HttpClientHandler() { _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled) + if (DiagnosticsHandler.IsGloballyEnabled()) { _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); } From ede3733b1cf5902899e56caf748492ed993c98c8 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 8 Jul 2021 23:32:15 -0700 Subject: [PATCH 12/72] Enregister structs on win x64. (#55045) * Enreg structs x64 windows. * try to get zero diffs on other platforms. * fix comment --- src/coreclr/jit/codegencommon.cpp | 14 ++++---------- src/coreclr/jit/codegenlinear.cpp | 16 +++++----------- src/coreclr/jit/compiler.h | 11 ++++++++++- src/coreclr/jit/jitconfigvalues.h | 8 ++++++-- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/lower.cpp | 23 ++++++++++++----------- src/coreclr/jit/lowerxarch.cpp | 6 ++++++ src/coreclr/jit/lsra.cpp | 2 +- 8 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 78926e64fda65..99852d7f44612 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4355,16 +4355,10 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere #endif // defined(UNIX_AMD64_ABI) noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg); -#ifndef TARGET_64BIT -#ifndef TARGET_ARM - // Right now we think that incoming arguments are not pointer sized. When we eventually - // understand the calling convention, this still won't be true. But maybe we'll have a better - // idea of how to ignore it. - - // On Arm, a long can be passed in register - noway_assert(genTypeSize(genActualType(varDsc->TypeGet())) == TARGET_POINTER_SIZE); -#endif -#endif // TARGET_64BIT +#ifdef TARGET_X86 + // On x86 we don't enregister args that are not pointer sized. + noway_assert(genTypeSize(varDsc->GetActualRegisterType()) == TARGET_POINTER_SIZE); +#endif // TARGET_X86 noway_assert(varDsc->lvIsInReg() && !regArgTab[argNum].circular); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 8bf1f852ffd4d..eb942332554f3 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1191,12 +1191,12 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) assert(spillType != TYP_UNDEF); // TODO-Cleanup: The following code could probably be further merged and cleaned up. -#ifdef TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) // Load local variable from its home location. // In most cases the tree type will indicate the correct type to use for the load. // However, if it is NOT a normalizeOnLoad lclVar (i.e. NOT a small int that always gets - // widened when loaded into a register), and its size is not the same as genActualType of - // the type of the lclVar, then we need to change the type of the tree node when loading. + // widened when loaded into a register), and its size is not the same as the actual register type + // of the lclVar, then we need to change the type of the tree node when loading. // This situation happens due to "optimizations" that avoid a cast and // simply retype the node when using long type lclVar as an int. // While loading the int in that case would work for this use of the lclVar, if it is @@ -1210,13 +1210,6 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) assert(!varTypeIsGC(varDsc)); spillType = lclActualType; } -#elif defined(TARGET_ARM64) - var_types targetType = unspillTree->gtType; - if (spillType != genActualType(varDsc->lvType) && !varTypeIsGC(spillType) && !varDsc->lvNormalizeOnLoad()) - { - assert(!varTypeIsGC(varDsc)); - spillType = genActualType(varDsc->lvType); - } #elif defined(TARGET_ARM) // No normalizing for ARM #else @@ -1465,7 +1458,8 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()]; if (varDsc->GetRegNum() != REG_STK) { - inst_Mov(tree->TypeGet(), tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true); + var_types regType = varDsc->GetRegisterType(lcl); + inst_Mov(regType, tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true); } } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 639b67bb15c94..f816e6917a604 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1016,11 +1016,20 @@ class LclVarDsc var_types GetActualRegisterType() const; - bool IsEnregisterable() const + bool IsEnregisterableType() const { return GetRegisterType() != TYP_UNDEF; } + bool IsEnregisterableLcl() const + { + if (lvDoNotEnregister) + { + return false; + } + return IsEnregisterableType(); + } + bool CanBeReplacedWithItsField(Compiler* comp) const; #ifdef DEBUG diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index cc10adc0b8809..7254e205fbc48 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -552,8 +552,12 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave #endif // defined(TARGET_ARM64) #endif // DEBUG -// Allow to enregister locals with struct type. -CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) +CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 1) // Allow to enregister locals with struct type. +#else +CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) // Don't allow to enregister locals with struct type + // yet. +#endif #undef CONFIG_INTEGER #undef CONFIG_STRING diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 603c6747c215c..b2ca56b406ba4 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3493,7 +3493,7 @@ void Compiler::lvaSortByRefCount() { varDsc->lvTracked = 0; } - else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct) + else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct && !compEnregStructLocals()) { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d2a0191648e18..9499ac5d81782 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3160,7 +3160,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) #endif // !WINDOWS_AMD64_ABI convertToStoreObj = false; } - else if (!varDsc->IsEnregisterable()) + else if (!varDsc->IsEnregisterableType()) { convertToStoreObj = true; } @@ -3418,11 +3418,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) lclNum = fieldLclNum; varDsc = comp->lvaGetDesc(lclNum); } - else if (!varDsc->lvRegStruct && !varTypeIsEnregisterable(varDsc)) - + else if (varDsc->lvPromoted) { - // TODO-1stClassStructs: We can no longer promote or enregister this struct, - // since it is referenced as a whole. + // TODO-1stClassStructs: We can no longer independently promote + // or enregister this struct, since it is referenced as a whole. comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_BlockOp)); } @@ -3434,9 +3433,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) } else { - var_types lclVarType = varDsc->GetRegisterType(lclVar); + const var_types lclVarType = varDsc->GetRegisterType(lclVar); assert(lclVarType != TYP_UNDEF); - lclVar->ChangeType(lclVarType); + const var_types actualType = genActualType(lclVarType); + lclVar->ChangeType(actualType); if (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(lclVarType)) { @@ -4039,12 +4039,12 @@ void Lowering::InsertPInvokeMethodProlog() GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, argList); // some sanity checks on the frame list root vardsc - LclVarDsc* varDsc = &comp->lvaTable[comp->info.compLvFrameListRoot]; + const unsigned lclNum = comp->info.compLvFrameListRoot; + const LclVarDsc* varDsc = comp->lvaGetDesc(lclNum); noway_assert(!varDsc->lvIsParam); noway_assert(varDsc->lvType == TYP_I_IMPL); - GenTree* store = - new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, comp->info.compLvFrameListRoot); + GenTree* store = new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, lclNum); store->AsOp()->gtOp1 = call; store->gtFlags |= GTF_VAR_DEF; @@ -4065,6 +4065,7 @@ void Lowering::InsertPInvokeMethodProlog() GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP); storeSP->gtOp1 = PhysReg(REG_SPBASE); storeSP->gtFlags |= GTF_VAR_DEF; + comp->lvaSetVarDoNotEnregister(comp->lvaInlinedPInvokeFrameVar DEBUGARG(Compiler::DNER_LocalField)); firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeSP)); DISPTREERANGE(firstBlockRange, storeSP); @@ -6534,7 +6535,7 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret) assert(varDsc->lvIsMultiRegRet || (varDsc->lvIsHfa() && varTypeIsValidHfaType(varDsc->lvType))); // Mark var as contained if not enregisterable. - if (!varTypeIsEnregisterable(op1)) + if (!varDsc->IsEnregisterableLcl()) { if (!op1->IsMultiRegLclVar()) { diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 55bfab94f6f5f..ed889f7f383bc 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -269,6 +269,12 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // address, not knowing that GT_IND is part of a block op that has containment restrictions. src->AsIndir()->Addr()->ClearContained(); } + else if (src->OperIs(GT_LCL_VAR)) + { + // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register. + const unsigned srcLclNum = src->AsLclVar()->GetLclNum(); + comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } if (blkNode->OperIs(GT_STORE_OBJ)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index ddc5442517466..f810ca3d3aa35 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -1490,7 +1490,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc) // or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning") // references when using the general GC encoding. unsigned lclNum = (unsigned)(varDsc - compiler->lvaTable); - if (varDsc->lvAddrExposed || !varDsc->IsEnregisterable() || + if (varDsc->lvAddrExposed || !varDsc->IsEnregisterableType() || (!compiler->compEnregStructLocals() && (varDsc->lvType == TYP_STRUCT))) { #ifdef DEBUG From d431d6a15726797965336176efe9ae15aba241fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Fri, 9 Jul 2021 09:41:39 +0200 Subject: [PATCH 13/72] H/3 stress (#55098) * Fixed event source name * Updated docker files, updated stress test to H/3 * SSL stress updated as well * Fixed docker and compilation warnings * H/3 fixes * Fixed nano-server version and nuget reference * Debian stable version revert: bullseye --> buster * Use msquic package in Linux stress container * Bullseye is back, needs 6.0 SDK current preview * Fixed pulling images in docker compose * Disabled problematic error in H/2 --- .../libraries-sdk-aspnetcore.linux.Dockerfile | 2 +- ...ibraries-sdk-aspnetcore.windows.Dockerfile | 2 +- eng/docker/libraries-sdk.linux.Dockerfile | 2 +- eng/docker/libraries-sdk.windows.Dockerfile | 2 +- eng/pipelines/libraries/stress/http.yml | 10 +++++++++- .../HttpStress/ClientOperations.cs | 10 +++++++++- .../tests/StressTests/HttpStress/Dockerfile | 14 +++++++++++++- .../StressTests/HttpStress/HttpStress.csproj | 3 ++- .../tests/StressTests/HttpStress/NuGet.config | 7 +++++++ .../tests/StressTests/HttpStress/Program.cs | 4 ++++ .../StressTests/HttpStress/StressClient.cs | 11 ++++++++--- .../StressTests/HttpStress/StressServer.cs | 19 ++++++++++++++++--- .../StressTests/HttpStress/windows.Dockerfile | 2 +- .../System/Net/Quic/NetEventSource.Quic.cs | 2 +- .../tests/StressTests/SslStress/Dockerfile | 4 ++-- .../StressTests/SslStress/SslStress.csproj | 7 +++---- .../SslStress/run-docker-compose.sh | 7 +++++++ .../StressTests/SslStress/windows.Dockerfile | 4 ++-- 18 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config diff --git a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile index 08adb4359e05c..2a232ee452418 100644 --- a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile +++ b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile @@ -1,6 +1,6 @@ # Builds and copies library artifacts into target dotnet sdk image ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754 -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $BUILD_BASE_IMAGE as corefxbuild diff --git a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile index dd306fc4ff126..9fcb11a9a0c9e 100644 --- a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile +++ b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile @@ -1,6 +1,6 @@ # escape=` # Simple Dockerfile which copies library build artifacts into target dotnet sdk image -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE as target ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost" diff --git a/eng/docker/libraries-sdk.linux.Dockerfile b/eng/docker/libraries-sdk.linux.Dockerfile index 1d704ecbc42b3..fd4f071da198d 100644 --- a/eng/docker/libraries-sdk.linux.Dockerfile +++ b/eng/docker/libraries-sdk.linux.Dockerfile @@ -1,6 +1,6 @@ # Builds and copies library artifacts into target dotnet sdk image ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754 -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $BUILD_BASE_IMAGE as corefxbuild diff --git a/eng/docker/libraries-sdk.windows.Dockerfile b/eng/docker/libraries-sdk.windows.Dockerfile index 564378f446729..c8d993b18d389 100644 --- a/eng/docker/libraries-sdk.windows.Dockerfile +++ b/eng/docker/libraries-sdk.windows.Dockerfile @@ -1,6 +1,6 @@ # escape=` # Simple Dockerfile which copies clr and library build artifacts into target dotnet sdk image -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE as target ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost" diff --git a/eng/pipelines/libraries/stress/http.yml b/eng/pipelines/libraries/stress/http.yml index 08b7611d21159..ba3a9f875f9e9 100644 --- a/eng/pipelines/libraries/stress/http.yml +++ b/eng/pipelines/libraries/stress/http.yml @@ -25,7 +25,7 @@ variables: jobs: - job: linux displayName: Docker Linux - timeoutInMinutes: 150 + timeoutInMinutes: 180 pool: name: NetCorePublic-Pool queue: BuildPool.Ubuntu.1804.Amd64.Open @@ -47,6 +47,14 @@ jobs: name: buildStress displayName: Build HttpStress + - bash: | + cd '$(httpStressProject)' + export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 3.0" + export HTTPSTRESS_SERVER_ARGS="$HTTPSTRESS_SERVER_ARGS -http 3.0" + docker-compose up --abort-on-container-exit --no-color + displayName: Run HttpStress - HTTP 3.0 + condition: and(eq(variables['buildRuntime.succeeded'], 'true'), eq(variables['buildStress.succeeded'], 'true')) + - bash: | cd '$(httpStressProject)' export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 2.0" diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs index 2a0f211808b99..5708e48277eb9 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs @@ -42,7 +42,8 @@ public RequestContext(Configuration config, HttpClient httpClient, Random random public int TaskNum { get; } public bool IsCancellationRequested { get; private set; } - public Version HttpVersion => _config.HttpVersion; + public Version HttpVersion => _client.DefaultRequestVersion; + public HttpVersionPolicy VersionPolicy => _client.DefaultVersionPolicy; public int MaxRequestParameters => _config.MaxParameters; public int MaxRequestUriSize => _config.MaxRequestUriSize; public int MaxRequestHeaderCount => _config.MaxRequestHeaderCount; @@ -54,6 +55,7 @@ public RequestContext(Configuration config, HttpClient httpClient, Random random public async Task SendAsync(HttpRequestMessage request, HttpCompletionOption httpCompletion = HttpCompletionOption.ResponseContentRead, CancellationToken? token = null) { request.Version = HttpVersion; + request.VersionPolicy = VersionPolicy; if (token != null) { @@ -480,6 +482,12 @@ public static (string name, Func operation)[] Operations = private static void ValidateStatusCode(HttpResponseMessage m, HttpStatusCode expectedStatus = HttpStatusCode.OK) { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55261")] + if (m.StatusCode == HttpStatusCode.InternalServerError) + { + throw new Exception("IGNORE"); + } + if (m.StatusCode != expectedStatus) { throw new Exception($"Expected status code {expectedStatus}, got {m.StatusCode}"); diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile index 00b1dd4e35e7e..a097b5033d083 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile @@ -1,4 +1,4 @@ -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $SDK_BASE_IMAGE RUN echo "DOTNET_SDK_VERSION="$DOTNET_SDK_VERSION @@ -7,6 +7,18 @@ RUN echo "DOTNET_VERSION="$DOTNET_VERSION WORKDIR /app COPY . . +# Pulling the msquic Debian package from msquic-ci public pipeline and from a hardcoded build. +# Note that this is a temporary solution until we have properly published Linux packages. +# Also note that in order to update to a newer msquic build, you have update this link. +ARG MSQUIC_PACKAGE=libmsquic_1.5.0_amd64.deb +ARG PACKAGES_DIR=LinuxPackages +RUN wget 'https://dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_apis/build/builds/1223883/artifacts?artifactName=LinuxPackages&api-version=6.0&%24format=zip' -O "$PACKAGES_DIR".zip +RUN apt-get update +RUN apt-get install unzip +RUN unzip $PACKAGES_DIR.zip +RUN dpkg -i $PACKAGES_DIR/$MSQUIC_PACKAGE +RUN rm -rf $PACKAGES_DIR* + ARG CONFIGURATION=Release RUN dotnet build -c $CONFIGURATION diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj index 57cbb0859dc80..db333cc634f6c 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 preview enable @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config new file mode 100644 index 0000000000000..0992c432038a9 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs index 70d2cdbf14907..407c0acd0db25 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs @@ -9,10 +9,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Versioning; using System.Threading.Tasks; using System.Net; using HttpStress; +[assembly:SupportedOSPlatform("windows")] +[assembly:SupportedOSPlatform("linux")] + /// /// Simple HttpClient stress app that launches Kestrel in-proc and runs many concurrent requests of varying types against it. /// diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs index 17db81103603d..be0dd1c42b063 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs @@ -65,8 +65,8 @@ HttpMessageHandler CreateHttpHandler() } } - return new HttpClient(CreateHttpHandler()) - { + return new HttpClient(CreateHttpHandler()) + { BaseAddress = _baseAddress, Timeout = _config.DefaultTimeout, DefaultRequestVersion = _config.HttpVersion, @@ -209,6 +209,11 @@ async Task RunWorker(int taskNum) { _aggregator.RecordCancellation(opIndex, stopwatch.Elapsed); } + catch (Exception e) when (e.Message == "IGNORE") + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55261")] + // See ClientOperations.ValidateStatusCode + } catch (Exception e) { _aggregator.RecordFailure(e, opIndex, stopwatch.Elapsed, requestContext.IsCancellationRequested, taskNum: taskNum, iteration: i); @@ -283,7 +288,7 @@ public void RecordCancellation(int operationIndex, TimeSpan elapsed) public void RecordFailure(Exception exn, int operationIndex, TimeSpan elapsed, bool isCancelled, int taskNum, long iteration) { DateTime timestamp = DateTime.Now; - + Interlocked.Increment(ref _totalRequests); Interlocked.Increment(ref _failures[operationIndex]); diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs index 6012965d2a509..32927f7b1ae12 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs @@ -45,7 +45,7 @@ public StressServer(Configuration configuration) (string scheme, string hostname, int port) = ParseServerUri(configuration.ServerUri); IWebHostBuilder host = WebHost.CreateDefaultBuilder(); - if (configuration.UseHttpSys) + if (configuration.UseHttpSys && OperatingSystem.IsWindows()) { // Use http.sys. This requires additional manual configuration ahead of time; // see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys?view=aspnetcore-2.2#configure-windows-server. @@ -108,16 +108,29 @@ void ConfigureListenOptions(ListenOptions listenOptions) } listenOptions.UseHttps(cert); } + if (configuration.HttpVersion == HttpVersion.Version30) + { + listenOptions.Protocols = HttpProtocols.Http3; + } } else { listenOptions.Protocols = - configuration.HttpVersion == new Version(2,0) ? + configuration.HttpVersion == HttpVersion.Version20 ? HttpProtocols.Http2 : HttpProtocols.Http1 ; } } }); + + if (configuration.HttpVersion == HttpVersion.Version30) + { + host = host.UseQuic(options => + { + options.Alpn = "h3-29"; + options.IdleTimeout = TimeSpan.FromMinutes(1); + }); + } }; LoggerConfiguration loggerConfiguration = new LoggerConfiguration(); @@ -161,7 +174,7 @@ void ConfigureListenOptions(ListenOptions listenOptions) private static void MapRoutes(IEndpointRouteBuilder endpoints) { var loggerFactory = endpoints.ServiceProvider.GetService(); - var logger = loggerFactory.CreateLogger(); + var logger = loggerFactory?.CreateLogger(); var head = new[] { "HEAD" }; endpoints.MapGet("/", async context => diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile index 43f1da1deca24..17cb0567199a1 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile @@ -1,5 +1,5 @@ # escape=` -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE # Use powershell as the default shell diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs index d86d7c6fc7712..66d66f0e40987 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs @@ -5,7 +5,7 @@ namespace System.Net { - [EventSource(Name = "Microsoft-System-Net-Quic")] + [EventSource(Name = "Private.InternalDiagnostics.System.Net.Quic")] internal sealed partial class NetEventSource : EventSource { } diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile b/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile index dcf3b9ecc9f9e..8e97f642a73b4 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile @@ -1,4 +1,4 @@ -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $SDK_BASE_IMAGE RUN echo "DOTNET_SDK_VERSION="$DOTNET_SDK_VERSION @@ -6,7 +6,7 @@ RUN echo "DOTNET_VERSION="$DOTNET_VERSION WORKDIR /app COPY . . -WORKDIR /app/System.Net.Security/tests/StressTests/SslStress +WORKDIR /app/System.Net.Security/tests/StressTests/SslStress ARG CONFIGURATION=Release RUN dotnet build -c $CONFIGURATION diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj b/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj index 3221dd6cd13cb..8b0a7a0aea188 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj @@ -1,15 +1,14 @@ Exe - net5.0 + net6.0 enable - + - + diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh b/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh index e18b80fca1dc3..91872f5e8a2c9 100755 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh @@ -89,6 +89,13 @@ fi compose_file="$scriptroot/docker-compose.yml" +if ! docker-compose --file "$compose_file" pull client; then + exit $? +fi +if ! docker-compose --file "$compose_file" pull server; then + exit $? +fi + if ! docker-compose --file "$compose_file" build $build_args; then exit $? fi diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile b/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile index 02e23f5c1c9a3..a1449eb4d5415 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile @@ -1,5 +1,5 @@ # escape=` -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE # Use powershell as the default shell @@ -10,7 +10,7 @@ RUN echo "DOTNET_VERSION="$env:DOTNET_VERSION WORKDIR /app COPY . . -WORKDIR /app/System.Net.Security/tests/StressTests/SslStress +WORKDIR /app/System.Net.Security/tests/StressTests/SslStress ARG CONFIGURATION=Release RUN dotnet build -c $env:CONFIGURATION From 46a860877ab143c8ae41d622a12129943e85b235 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 9 Jul 2021 05:37:24 -0400 Subject: [PATCH 14/72] [mono] Enable icall export for ios device builds. (#55344) Fixes https://github.com/dotnet/runtime/issues/55000 --- src/mono/mono.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/mono.proj b/src/mono/mono.proj index cc9fc41b54629..a329c0ac29940 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -347,6 +347,7 @@ <_MonoCMakeArgs Include="-DENABLE_VISIBILITY_HIDDEN=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_SIGALTSTACK=0"/> + <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCFLAGS Include="-Werror=partial-availability" /> <_MonoCFLAGS Condition="'$(TargetstvOS)' == 'true'" Include="-fno-gnu-inline-asm" /> <_MonoCFLAGS Include="-fexceptions" /> From e4e942f91e6e2e32988a610c0e7f754df7df56b1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 06:32:54 -0400 Subject: [PATCH 15/72] Add private Task.StateFlags property to help with debugging (#55297) It's a pain when you want to debug something with tasks, you look at its m_stateFlags, and you're met with an integer you have to decode by looking at consts defined in Task. This changes those consts to be an enum and adds a private property that returns the flags as that enum, to help with debugging. --- .../src/ILLink/ILLink.Descriptors.Shared.xml | 1 + .../src/System/Threading/Tasks/Future.cs | 8 +- .../src/System/Threading/Tasks/Task.cs | 234 +++++++++--------- .../Threading/Tasks/TaskContinuation.cs | 4 +- .../System/Threading/Tasks/TaskScheduler.cs | 4 +- 5 files changed, 126 insertions(+), 125 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index 1989afaeda1bd..401fe001de280 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -13,6 +13,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs index f2dd5b2e37efa..ac42990b31d1a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @@ -378,8 +378,8 @@ internal bool TrySetResult(TResult? result) // been recorded, and (4) Cancellation has not been requested. // // If the reservation is successful, then set the result and finish completion processing. - if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + if (AtomicStateUpdate((int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { m_result = result; @@ -390,7 +390,7 @@ internal bool TrySetResult(TResult? result) // However, that goes through a windy code path, involves many non-inlineable functions // and which can be summarized more concisely with the following snippet from // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult. - Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); + Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.RanToCompletion); ContingentProperties? props = m_contingentProperties; if (props != null) { @@ -425,7 +425,7 @@ internal void DangerousSetResult(TResult result) else { m_result = result; - m_stateFlags |= TASK_STATE_RAN_TO_COMPLETION; + m_stateFlags |= (int)TaskStateFlags.RanToCompletion; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 923c4cf71bcf4..a64dc00842991 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -138,36 +138,36 @@ public class Task : IAsyncResult, IDisposable private Task? ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags - - // State constants for m_stateFlags; - // The bits of m_stateFlags are allocated as follows: - // 0x40000000 - TaskBase state flag - // 0x3FFF0000 - Task state flags - // 0x0000FF00 - internal TaskCreationOptions flags - // 0x000000FF - publicly exposed TaskCreationOptions flags - // - // See TaskCreationOptions for bit values associated with TaskCreationOptions - // - private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111 - internal const int TASK_STATE_STARTED = 0x10000; // bin: 0000 0000 0000 0001 0000 0000 0000 0000 - internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; // bin: 0000 0000 0000 0010 0000 0000 0000 0000 - internal const int TASK_STATE_DISPOSED = 0x40000; // bin: 0000 0000 0000 0100 0000 0000 0000 0000 - internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; // bin: 0000 0000 0000 1000 0000 0000 0000 0000 - internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; // bin: 0000 0000 0001 0000 0000 0000 0000 0000 - internal const int TASK_STATE_FAULTED = 0x200000; // bin: 0000 0000 0010 0000 0000 0000 0000 0000 - internal const int TASK_STATE_CANCELED = 0x400000; // bin: 0000 0000 0100 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; // bin: 0000 0000 1000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; // bin: 0000 0001 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; // bin: 0000 0010 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; // bin: 0000 0100 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; // bin: 0001 0000 0000 0000 0000 0000 0000 0000 - // This could be moved to InternalTaskOptions enum - internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; // bin: 0010 0000 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; // bin: 0100 0000 0000 0000 0000 0000 0000 0000 - - // A mask for all of the final states a task may be in. - // SOS DumpAsync command depends on these values. - private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION; + private TaskStateFlags StateFlags => (TaskStateFlags)(m_stateFlags & ~(int)TaskStateFlags.OptionsMask); // Private property used to help with debugging + + [Flags] + internal enum TaskStateFlags + { + // State constants for m_stateFlags; + // The bits of m_stateFlags are allocated as follows: + // 0x7FFF0000 - Task state flags + // 0x0000FF00 - internal TaskCreationOptions flags + // 0x000000FF - publicly exposed TaskCreationOptions flags + // See TaskCreationOptions for bit values associated with TaskCreationOptions + + Started = 0x10000, // bin: 0000 0000 0000 0001 0000 0000 0000 0000 + DelegateInvoked = 0x20000, // bin: 0000 0000 0000 0010 0000 0000 0000 0000 + Disposed = 0x40000, // bin: 0000 0000 0000 0100 0000 0000 0000 0000 + ExceptionObservedByParent = 0x80000, // bin: 0000 0000 0000 1000 0000 0000 0000 0000 + CancellationAcknowledged = 0x100000, // bin: 0000 0000 0001 0000 0000 0000 0000 0000 + Faulted = 0x200000, // bin: 0000 0000 0010 0000 0000 0000 0000 0000 + Canceled = 0x400000, // bin: 0000 0000 0100 0000 0000 0000 0000 0000 + WaitingOnChildren = 0x800000, // bin: 0000 0000 1000 0000 0000 0000 0000 0000 + RanToCompletion = 0x1000000, // bin: 0000 0001 0000 0000 0000 0000 0000 0000 + WaitingForActivation = 0x2000000, // bin: 0000 0010 0000 0000 0000 0000 0000 0000 + CompletionReserved = 0x4000000, // bin: 0000 0100 0000 0000 0000 0000 0000 0000 + WaitCompletionNotification = 0x10000000, // bin: 0001 0000 0000 0000 0000 0000 0000 0000 + ExecutionContextIsNull = 0x20000000, // bin: 0010 0000 0000 0000 0000 0000 0000 0000 + TaskScheduledWasFired = 0x40000000, // bin: 0100 0000 0000 0000 0000 0000 0000 0000 + + CompletedMask = Canceled | Faulted | RanToCompletion, // A mask for all of the final states a task may be in. SOS DumpAsync command depends on these values. + OptionsMask = 0xFFFF, // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111 + } // Values for ContingentProperties.m_internalCancellationRequested. private const int CANCELLATION_REQUESTED = 0x1; @@ -294,7 +294,7 @@ internal Task(bool canceled, TaskCreationOptions creationOptions, CancellationTo int optionFlags = (int)creationOptions; if (canceled) { - m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags; + m_stateFlags = (int)TaskStateFlags.Canceled | (int)TaskStateFlags.CancellationAcknowledged | optionFlags; m_contingentProperties = new ContingentProperties() // can't have children, so just instantiate directly { m_cancellationToken = ct, @@ -303,14 +303,14 @@ internal Task(bool canceled, TaskCreationOptions creationOptions, CancellationTo } else { - m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags; + m_stateFlags = (int)TaskStateFlags.RanToCompletion | optionFlags; } } /// Constructor for use with promise-style tasks that aren't configurable. internal Task() { - m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | (int)InternalTaskOptions.PromiseTask; + m_stateFlags = (int)TaskStateFlags.WaitingForActivation | (int)InternalTaskOptions.PromiseTask; } // Special constructor for use with promise-style tasks. @@ -559,10 +559,10 @@ internal void TaskConstructorCore(Delegate? action, object? state, CancellationT // Assign options to m_stateAndOptionsFlag. Debug.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags"); - Debug.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits"); + Debug.Assert((((int)creationOptions) | (int)TaskStateFlags.OptionsMask) == (int)TaskStateFlags.OptionsMask, "TaskConstructorCore: options take too many bits"); int tmpFlags = (int)creationOptions | (int)internalOptions; // one write to the volatile m_stateFlags instead of two when setting the above options m_stateFlags = m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != 0 ? - tmpFlags | TASK_STATE_WAITINGFORACTIVATION : + tmpFlags | (int)TaskStateFlags.WaitingForActivation : tmpFlags; // Now is the time to add the new task to the children list @@ -674,8 +674,8 @@ private void AssignCancellationToken(CancellationToken cancellationToken, Task? // a read of the volatile m_stateFlags field. internal static TaskCreationOptions OptionsMethod(int flags) { - Debug.Assert((OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get"); - return (TaskCreationOptions)(flags & OptionsMask); + Debug.Assert(((int)TaskStateFlags.OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get"); + return (TaskCreationOptions)(flags & (int)TaskStateFlags.OptionsMask); } // Atomically OR-in newBits to m_stateFlags, while making sure that @@ -720,7 +720,7 @@ internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags) } /// - /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit. + /// Sets or clears the TaskStateFlags.WaitCompletionNotification state bit. /// The debugger sets this bit to aid it in "stepping out" of an async method body. /// If enabled is true, this must only be called on a task that has not yet been completed. /// If enabled is false, this may be called on completed tasks. @@ -734,15 +734,15 @@ internal void SetNotificationForWaitCompletion(bool enabled) if (enabled) { - // Atomically set the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit - bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION, - TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED); + // Atomically set the TaskStateFlags.WaitCompletionNotification bit + bool success = AtomicStateUpdate((int)TaskStateFlags.WaitCompletionNotification, + (int)TaskStateFlags.CompletedMask | (int)TaskStateFlags.CompletionReserved); Debug.Assert(success, "Tried to set enabled on completed Task"); } else { - // Atomically clear the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit - Interlocked.And(ref m_stateFlags, ~TASK_STATE_WAIT_COMPLETION_NOTIFICATION); + // Atomically clear the TaskStateFlags.WaitCompletionNotification bit + Interlocked.And(ref m_stateFlags, ~(int)TaskStateFlags.WaitCompletionNotification); } } @@ -785,8 +785,8 @@ internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task?[] tasks internal bool IsWaitNotificationEnabledOrNotRanToCompletion { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (m_stateFlags & (Task.TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task.TASK_STATE_RAN_TO_COMPLETION)) - != Task.TASK_STATE_RAN_TO_COMPLETION; + get => (m_stateFlags & ((int)TaskStateFlags.WaitCompletionNotification | (int)TaskStateFlags.RanToCompletion)) + != (int)TaskStateFlags.RanToCompletion; } /// @@ -812,7 +812,7 @@ internal bool IsWaitNotificationEnabledOrNotRanToCompletion /// Gets whether the task's debugger notification for wait completion bit is set. /// true if the bit is set; false if it's not set. internal bool IsWaitNotificationEnabled => // internal only to enable unit tests; would otherwise be private - (m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION) != 0; + (m_stateFlags & (int)TaskStateFlags.WaitCompletionNotification) != 0; /// Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized. /// All joins with a task should end up calling this if their debugger notification bit is set. @@ -834,14 +834,14 @@ private void NotifyDebuggerOfWaitCompletion() // Atomically mark a Task as started while making sure that it is not canceled. internal bool MarkStarted() { - return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED); + return AtomicStateUpdate((int)TaskStateFlags.Started, (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Started); } internal void FireTaskScheduledIfNeeded(TaskScheduler ts) { - if ((m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0) + if ((m_stateFlags & (int)TaskStateFlags.TaskScheduledWasFired) == 0) { - m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED; + m_stateFlags |= (int)TaskStateFlags.TaskScheduledWasFired; if (TplEventSource.Log.IsEnabled()) { @@ -1119,7 +1119,7 @@ internal void InternalRunSynchronously(TaskScheduler scheduler, bool waitForComp } else { - Debug.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Canceled) != 0, "Task.RunSynchronously: expected TaskStateFlags.Canceled to be set"); // Can't call this method on canceled task. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted); } @@ -1269,13 +1269,13 @@ public TaskStatus Status // execution of this method. int sf = m_stateFlags; - if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted; - else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled; - else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion; - else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete; - else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running; - else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun; - else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation; + if ((sf & (int)TaskStateFlags.Faulted) != 0) rval = TaskStatus.Faulted; + else if ((sf & (int)TaskStateFlags.Canceled) != 0) rval = TaskStatus.Canceled; + else if ((sf & (int)TaskStateFlags.RanToCompletion) != 0) rval = TaskStatus.RanToCompletion; + else if ((sf & (int)TaskStateFlags.WaitingOnChildren) != 0) rval = TaskStatus.WaitingForChildrenToComplete; + else if ((sf & (int)TaskStateFlags.DelegateInvoked) != 0) rval = TaskStatus.Running; + else if ((sf & (int)TaskStateFlags.Started) != 0) rval = TaskStatus.WaitingToRun; + else if ((sf & (int)TaskStateFlags.WaitingForActivation) != 0) rval = TaskStatus.WaitingForActivation; else rval = TaskStatus.Created; return rval; @@ -1295,7 +1295,7 @@ public TaskStatus Status /// public bool IsCanceled => // Return true if canceled bit is set and faulted bit is not set - (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED; + (m_stateFlags & ((int)TaskStateFlags.Canceled | (int)TaskStateFlags.Faulted)) == (int)TaskStateFlags.Canceled; /// /// Returns true if this task has a cancellation token and it was signaled. @@ -1353,7 +1353,7 @@ internal CancellationToken CancellationToken /// /// Gets whether this threw an OperationCanceledException while its CancellationToken was signaled. /// - internal bool IsCancellationAcknowledged => (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; + internal bool IsCancellationAcknowledged => (m_stateFlags & (int)TaskStateFlags.CancellationAcknowledged) != 0; /// /// Gets whether this Task has completed. @@ -1377,10 +1377,10 @@ public bool IsCompleted // rather than reading the volatile m_stateFlags field. private static bool IsCompletedMethod(int flags) { - return (flags & TASK_STATE_COMPLETED_MASK) != 0; + return (flags & (int)TaskStateFlags.CompletedMask) != 0; } - public bool IsCompletedSuccessfully => (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; + public bool IsCompletedSuccessfully => (m_stateFlags & (int)TaskStateFlags.CompletedMask) == (int)TaskStateFlags.RanToCompletion; /// /// Gets the TaskCreationOptions used @@ -1418,7 +1418,7 @@ WaitHandle IAsyncResult.AsyncWaitHandle // forces allocation of a true WaitHandle when called. get { - bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0; + bool isDisposed = (m_stateFlags & (int)TaskStateFlags.Disposed) != 0; if (isDisposed) { ThrowHelper.ThrowObjectDisposedException(ExceptionResource.Task_ThrowIfDisposed); @@ -1513,18 +1513,18 @@ internal bool ExceptionRecorded /// public bool IsFaulted => // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted. - (m_stateFlags & TASK_STATE_FAULTED) != 0; + (m_stateFlags & (int)TaskStateFlags.Faulted) != 0; /// /// The captured execution context for the current task to run inside - /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise + /// If the TaskStateFlags.ExecutionContextIsNull flag is set, this means ExecutionContext.Capture returned null, otherwise /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context /// internal ExecutionContext? CapturedContext { get { - if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL) + if ((m_stateFlags & (int)TaskStateFlags.ExecutionContextIsNull) == (int)TaskStateFlags.ExecutionContextIsNull) { return null; } @@ -1538,7 +1538,7 @@ internal ExecutionContext? CapturedContext // There is no need to atomically set this bit because this set() method is only called during construction, and therefore there should be no contending accesses to m_stateFlags if (value == null) { - m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL; + m_stateFlags |= (int)TaskStateFlags.ExecutionContextIsNull; } else if (value != ExecutionContext.Default) // not the default context, then inflate the contingent properties and set it { @@ -1629,10 +1629,10 @@ protected virtual void Dispose(bool disposing) // We OR the flags to indicate the object has been disposed. The task // has already completed at this point, and the only conceivable race condition would - // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which + // be with the unsetting of the TaskStateFlags.WaitCompletionNotification flag, which // is extremely unlikely and also benign. (Worst case: we hit a breakpoint // twice instead of once in the debugger. Weird, but not lethal.) - m_stateFlags |= TASK_STATE_DISPOSED; + m_stateFlags |= (int)TaskStateFlags.Disposed; } ///////////// @@ -1641,17 +1641,17 @@ protected virtual void Dispose(bool disposing) /// /// Schedules the task for execution. /// - /// If true, TASK_STATE_STARTED bit is turned on in - /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set - /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This + /// If true, TaskStateFlags.Started bit is turned on in + /// an atomic fashion, making sure that TaskStateFlags.Canceled does not get set + /// underneath us. If false, TaskStateFlags.Started bit is OR-ed right in. This /// allows us to streamline things a bit for StartNew(), where competing cancellations /// are not a problem. internal void ScheduleAndStart(bool needsProtection) { Debug.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected"); - Debug.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Started) == 0, "task has already started"); - // Set the TASK_STATE_STARTED bit + // Set the TaskStateFlags.Started bit if (needsProtection) { if (!MarkStarted()) @@ -1662,7 +1662,7 @@ internal void ScheduleAndStart(bool needsProtection) } else { - m_stateFlags |= TASK_STATE_STARTED; + m_stateFlags |= (int)TaskStateFlags.Started; } if (s_asyncDebuggingEnabled) @@ -1915,7 +1915,7 @@ internal static void ThrowAsync(Exception exception, SynchronizationContext? tar /// /// Checks whether this is an attached task, and whether we are being called by the parent task. - /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that. + /// And sets the TaskStateFlags.ExceptionObservedByParent status flag based on that. /// /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children @@ -1932,21 +1932,21 @@ internal void UpdateExceptionObservedStatus() && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) && Task.InternalCurrent == parent) { - m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT; + m_stateFlags |= (int)TaskStateFlags.ExceptionObservedByParent; } } /// - /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set, + /// Checks whether the TaskStateFlags.ExceptionObservedByParent status flag is set, /// This will only be used by the implicit wait to prevent double throws /// /// - internal bool IsExceptionObservedByParent => (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0; + internal bool IsExceptionObservedByParent => (m_stateFlags & (int)TaskStateFlags.ExceptionObservedByParent) != 0; /// /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task. /// - internal bool IsDelegateInvoked => (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0; + internal bool IsDelegateInvoked => (m_stateFlags & (int)TaskStateFlags.DelegateInvoked) != 0; /// /// Signals completion of this particular task. @@ -2000,11 +2000,11 @@ private void FinishSlow(bool userDelegateExecute) // We have to use an atomic update for this and make sure not to overwrite a final state, // because at this very moment the last child's thread may be concurrently completing us. - // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task. - // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag, + // Otherwise we risk overwriting the TaskStateFlags.RanToCompletion, _CANCELED or _FAULTED bit which may have been set by that child task. + // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TaskStateFlags.WaitingOnChildren flag, // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo - AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION); + AtomicStateUpdate((int)TaskStateFlags.WaitingOnChildren, (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled | (int)TaskStateFlags.RanToCompletion); } // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw. @@ -2039,7 +2039,7 @@ private void FinishStageTwo() int completionState; if (ExceptionRecorded) { - completionState = TASK_STATE_FAULTED; + completionState = (int)TaskStateFlags.Faulted; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Error); @@ -2048,14 +2048,14 @@ private void FinishStageTwo() } else if (IsCancellationRequested && IsCancellationAcknowledged) { - // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation, + // We transition into the TaskStateFlags.Canceled final state if the task's CT was signalled for cancellation, // and the user delegate acknowledged the cancellation request by throwing an OCE, - // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED) + // and the task hasn't otherwise transitioned into faulted state. (TaskStateFlags.Faulted trumps TaskStateFlags.Canceled) // // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state), // then we regard it as a regular exception - completionState = TASK_STATE_CANCELED; + completionState = (int)TaskStateFlags.Canceled; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Canceled); @@ -2064,7 +2064,7 @@ private void FinishStageTwo() } else { - completionState = TASK_STATE_RAN_TO_COMPLETION; + completionState = (int)TaskStateFlags.RanToCompletion; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Completed); @@ -2124,7 +2124,7 @@ internal void NotifyParentIfPotentiallyAttachedTask() Task? parent = m_contingentProperties?.m_parent; if (parent != null && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) - && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0) + && (((TaskCreationOptions)(m_stateFlags & (int)TaskStateFlags.OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0) { parent.ProcessChildCompletion(this); } @@ -2227,9 +2227,9 @@ internal bool ExecuteEntry() // However we don't want this exception to be throw if the task was already canceled, because it's a // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) int previousState = 0; - if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, - TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, - ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) + if (!AtomicStateUpdate((int)TaskStateFlags.DelegateInvoked, + (int)TaskStateFlags.DelegateInvoked | (int)TaskStateFlags.CompletedMask, + ref previousState) && (previousState & (int)TaskStateFlags.Canceled) == 0) { // This task has already been invoked. Don't invoke it again. return false; @@ -2258,7 +2258,7 @@ internal bool ExecuteEntry() internal void ExecuteEntryUnsafe(Thread? threadPoolThread) // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent { // Remember that we started running the task delegate. - m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + m_stateFlags |= (int)TaskStateFlags.DelegateInvoked; if (!IsCancellationRequested & !IsCanceled) { @@ -2274,8 +2274,8 @@ internal void ExecuteEntryCancellationRequestedOrCanceled() { if (!IsCanceled) { - int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); - if ((prevState & TASK_STATE_CANCELED) == 0) + int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled); + if ((prevState & (int)TaskStateFlags.Canceled) == 0) { CancellationCleanupLogic(); } @@ -2727,7 +2727,7 @@ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) ThrowIfExceptional(true); } - Debug.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state."); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Faulted) == 0, "Task.Wait() completing when in Faulted state."); return true; } @@ -3060,7 +3060,7 @@ internal void InternalCancel() bool popped = false; // If started, and running in a task context, we can try to pop the chore. - if ((m_stateFlags & TASK_STATE_STARTED) != 0) + if ((m_stateFlags & (int)TaskStateFlags.Started) != 0) { TaskScheduler? ts = m_taskScheduler; try @@ -3081,24 +3081,24 @@ internal void InternalCancel() // Determine whether we need to clean up // This will be the case - // 1) if we were able to pop, and we are able to update task state to TASK_STATE_CANCELED + // 1) if we were able to pop, and we are able to update task state to TaskStateFlags.Canceled // 2) if the task seems to be yet unstarted, and we can transition to - // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or + // TaskStateFlags.Canceled before anyone else can transition into _STARTED or _CANCELED or // _RAN_TO_COMPLETION or _FAULTED - // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style + // Note that we do not check for TaskStateFlags.CompletionReserved. That only applies to promise-style // tasks, and a promise-style task should not enter into this codepath. bool mustCleanup = false; if (popped) { - // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where + // Include TaskStateFlags.DelegateInvoked in "illegal" bits to protect against the situation where // TS.TryDequeue() returns true but the task is still left on the queue. - mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED); + mustCleanup = AtomicStateUpdate((int)TaskStateFlags.Canceled, (int)TaskStateFlags.Canceled | (int)TaskStateFlags.DelegateInvoked); } - else if ((m_stateFlags & TASK_STATE_STARTED) == 0) + else if ((m_stateFlags & (int)TaskStateFlags.Started) == 0) { - mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, - TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION | - TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED); + mustCleanup = AtomicStateUpdate((int)TaskStateFlags.Canceled, + (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Started | (int)TaskStateFlags.RanToCompletion | + (int)TaskStateFlags.Faulted | (int)TaskStateFlags.DelegateInvoked); } // do the cleanup (i.e. set completion event and finish continuations) @@ -3125,12 +3125,12 @@ internal void InternalCancelContinueWithInitialState() // - it was canceled, which won't have happened without a token // - it was run as a continuation, which won't have happened because this method is only invoked once // As a result, we can take an optimized path that avoids inflating contingent properties. - const int IllegalFlags = TASK_STATE_STARTED | TASK_STATE_COMPLETED_MASK | TASK_STATE_DELEGATE_INVOKED; + const int IllegalFlags = (int)TaskStateFlags.Started | (int)TaskStateFlags.CompletedMask | (int)TaskStateFlags.DelegateInvoked; Debug.Assert((m_stateFlags & IllegalFlags) == 0, "The continuation was in an invalid state."); - Debug.Assert((m_stateFlags & TASK_STATE_WAITINGFORACTIVATION) != 0, "Expected continuation to be waiting for activation"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.WaitingForActivation) != 0, "Expected continuation to be waiting for activation"); Debug.Assert(m_contingentProperties is null || m_contingentProperties.m_cancellationToken == default); - m_stateFlags |= TASK_STATE_CANCELED; // no synchronization necessary, per above comment + m_stateFlags |= (int)TaskStateFlags.Canceled; // no synchronization necessary, per above comment CancellationCleanupLogic(); } @@ -3181,14 +3181,14 @@ internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, // And this method should be called at most once per task. internal void CancellationCleanupLogic() { - Debug.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved."); + Debug.Assert((m_stateFlags & ((int)TaskStateFlags.Canceled | (int)TaskStateFlags.CompletionReserved)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved."); // We'd like to be able to: // Debug.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set."); // However, there is a small window for a race condition. If someone calls Wait() between InternalCancel() and // here, that will set m_completionEvent, leading to a meaningless/harmless assertion. // This may have been set already, but we need to make sure. - Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); + Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled); // Fire completion event if it has been lazily initialized ContingentProperties? cp = Volatile.Read(ref m_contingentProperties); @@ -3216,7 +3216,7 @@ private void SetCancellationAcknowledged() Debug.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task"); Debug.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled"); - m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED; + m_stateFlags |= (int)TaskStateFlags.CancellationAcknowledged; } /// Completes a promise task as RanToCompletion. @@ -3227,8 +3227,8 @@ internal bool TrySetResult() Debug.Assert(m_action == null, "Task.TrySetResult(): non-null m_action"); if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { ContingentProperties? props = m_contingentProperties; if (props != null) @@ -3275,8 +3275,8 @@ internal bool TrySetException(object exceptionObject) // anyway. Some downstream logic may depend upon an inflated m_contingentProperties. EnsureContingentPropertiesInitialized(); if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + (int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { AddException(exceptionObject); // handles singleton exception or exception collection Finish(false); @@ -3315,8 +3315,8 @@ cancellationException is OperationCanceledException || // // If the reservation is successful, then record the cancellation and finish completion processing. if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION)) + (int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.RanToCompletion)) { RecordInternalCancellationRequest(tokenToRecord, cancellationException); CancellationCleanupLogic(); // perform cancellation cleanup actions diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs index 18feb9a226698..05576dae666ff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs @@ -216,7 +216,7 @@ protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtectio Debug.Assert(task != null); Debug.Assert(task.m_taskScheduler != null); - // Set the TASK_STATE_STARTED flag. This only needs to be done + // Set the TaskStateFlags.Started flag. This only needs to be done // if the task may be canceled or if someone else has a reference to it // that may try to execute it. if (needsProtection) @@ -226,7 +226,7 @@ protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtectio } else { - task.m_stateFlags |= Task.TASK_STATE_STARTED; + task.m_stateFlags |= (int)Task.TaskStateFlags.Started; } // Try to inline it but queue if we can't diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs index 2d901838707a4..e8d3ef133577f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs @@ -166,7 +166,7 @@ internal bool TryRunInline(Task task, bool taskWasPreviouslyQueued) { // Do not inline unstarted tasks (i.e., task.ExecutingTaskScheduler == null). // Do not inline TaskCompletionSource-style (a.k.a. "promise") tasks. - // No need to attempt inlining if the task body was already run (i.e. either TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bits set) + // No need to attempt inlining if the task body was already run (i.e. either TaskStateFlags.DelegateInvoked or TaskStateFlags.Canceled bits set) TaskScheduler? ets = task.ExecutingTaskScheduler; // Delegate cross-scheduler inlining requests to target scheduler @@ -189,7 +189,7 @@ internal bool TryRunInline(Task task, bool taskWasPreviouslyQueued) bool inlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued); - // If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set + // If the custom scheduler returned true, we should either have the TaskStateFlags.DelegateInvoked or TaskStateFlags.Canceled bit set // Otherwise the scheduler is buggy if (inlined && !(task.IsDelegateInvoked || task.IsCanceled)) { From 5af27a545d9246a8472bc087720f91bf0aa0eadc Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Fri, 9 Jul 2021 12:41:14 +0200 Subject: [PATCH 16/72] Remove few more ILLink warnings in System.Data.Common (#55335) --- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------------ .../src/System/Data/ColumnTypeConverter.cs | 3 +++ 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 2eda63ecd065c..9825521322ca8 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,18 +1,6 @@  - - ILLink - IL2026 - member - M:System.Data.ColumnTypeConverter.ConvertTo(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type) - - - ILLink - IL2060 - member - M:System.Data.DataRowExtensions.UnboxT`1.Create - ILLink IL2026 @@ -61,12 +49,6 @@ member M:System.Data.DataTable.WriteXmlCore(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataView.SetRowFilter(System.String) - ILLink IL2026 diff --git a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs index 015e87d0897fb..f259f711384be 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Data.SqlTypes; using System.Reflection; @@ -65,6 +66,8 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina /// /// Converts the given value object to the specified destination type. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "InstanceDescriptor calls GetType(string) on AssemblyQualifiedName of instance of type we already have in here.")] public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) From f1bf5eee390fa118441cae14a157773f1e2d751d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 06:46:29 -0400 Subject: [PATCH 17/72] Add Windows support to PosixSignal (#55333) * Add Windows support to PosixSignal * Address PR feedback --- .../Interop.SetConsoleCtrlHandler.Delegate.cs | 17 +++ .../Kernel32/Interop.SetConsoleCtrlHandler.cs | 9 +- .../src/Microsoft.Win32.SystemEvents.csproj | 4 +- .../System.Console/src/System.Console.csproj | 2 - .../System.Console/src/System/Console.cs | 41 +++--- .../src/System/ConsolePal.Android.cs | 9 -- .../src/System/ConsolePal.Unix.cs | 31 ----- .../src/System/ConsolePal.WebAssembly.cs | 9 -- .../src/System/ConsolePal.Windows.cs | 47 ------- .../src/System/ConsolePal.iOS.cs | 9 -- .../ref/System.Runtime.InteropServices.cs | 11 ++ .../src/Resources/Strings.resx | 5 +- .../src/System.Runtime.InteropServices.csproj | 24 ++-- .../Runtime/InteropServices/PosixSignal.cs | 28 ++++ .../InteropServices/PosixSignalContext.cs | 20 +-- .../PosixSignalRegistration.Browser.cs | 18 --- .../PosixSignalRegistration.Unix.cs | 58 +++----- .../PosixSignalRegistration.Unsupported.cs | 22 +++ .../PosixSignalRegistration.Windows.cs | 130 +++++++++++++++++- .../PosixSignalRegistration.cs | 38 +++++ ...ystem.Runtime.InteropServices.Tests.csproj | 12 +- .../PosixSignalContextTests.cs | 32 ++++- .../PosixSignalRegistrationTests.Browser.cs | 29 ++++ .../PosixSignalRegistrationTests.Unix.cs | 80 ++++------- .../PosixSignalRegistrationTests.Windows.cs | 30 ++++ .../PosixSignalRegistrationTests.cs | 74 ++++++++++ 26 files changed, 502 insertions(+), 287 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs new file mode 100644 index 0000000000000..159c26438c280 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + internal delegate bool ConsoleCtrlHandlerRoutine(int controlType); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs index c6ee59a77a2b9..112d5b4d5a89b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Win32.SafeHandles; -using System; using System.Runtime.InteropServices; internal static partial class Interop @@ -11,10 +9,11 @@ internal static partial class Kernel32 { internal const int CTRL_C_EVENT = 0; internal const int CTRL_BREAK_EVENT = 1; - - internal delegate bool ConsoleCtrlHandlerRoutine(int controlType); + internal const int CTRL_CLOSE_EVENT = 2; + internal const int CTRL_LOGOFF_EVENT = 5; + internal const int CTRL_SHUTDOWN_EVENT = 6; [DllImport(Libraries.Kernel32, SetLastError = true)] - internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove); + internal static extern unsafe bool SetConsoleCtrlHandler(delegate* unmanaged HandlerRoutine, bool Add); } } diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj index 2929b90c61a3a..97cc92a8ef32a 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj @@ -82,8 +82,8 @@ Link="Common\Interop\Windows\Kernel32\Interop.LoadLibrary.cs" /> - + - handler = HandlePosixSignal; + s_sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, handler); + s_sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, handler); } } } @@ -602,10 +607,13 @@ public static event ConsoleCancelEventHandler? CancelKeyPress lock (s_syncObject) { s_cancelCallbacks -= value; - if (s_registrar != null && s_cancelCallbacks == null) + + // If there are no more callbacks, unregister registered posix signal handlers. + if (s_cancelCallbacks == null) { - s_registrar.Unregister(); - s_registrar = null; + s_sigIntRegistration?.Dispose(); + s_sigQuitRegistration?.Dispose(); + s_sigIntRegistration = s_sigQuitRegistration = null; } } } @@ -960,18 +968,17 @@ public static void Write(string? value) Out.Write(value); } - internal static bool HandleBreakEvent(ConsoleSpecialKey controlKey, bool cancel = false) + private static void HandlePosixSignal(PosixSignalContext ctx) { - ConsoleCancelEventHandler? handler = s_cancelCallbacks; - if (handler == null) + Debug.Assert(ctx.Signal == PosixSignal.SIGINT || ctx.Signal == PosixSignal.SIGQUIT); + + if (s_cancelCallbacks is ConsoleCancelEventHandler handler) { - return false; + var args = new ConsoleCancelEventArgs(ctx.Signal == PosixSignal.SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak); + args.Cancel = ctx.Cancel; + handler(null, args); + ctx.Cancel = args.Cancel; } - - var args = new ConsoleCancelEventArgs(controlKey); - args.Cancel = cancel; - handler(null, args); - return args.Cancel; } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Android.cs b/src/libraries/System.Console/src/System/ConsolePal.Android.cs index 910a33ad82494..3777952799f3a 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Android.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Android.cs @@ -152,14 +152,5 @@ public static int WindowHeight public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index c438413c801cd..eacf15a203efd 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -1442,37 +1442,6 @@ public override void Flush() } } - internal sealed class ControlCHandlerRegistrar - { - private PosixSignalRegistration? _sigIntRegistration; - private PosixSignalRegistration? _sigQuitRegistration; - - internal unsafe void Register() - { - Debug.Assert(_sigIntRegistration is null); - - _sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, HandlePosixSignal); - _sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandlePosixSignal); - } - - internal void Unregister() - { - Debug.Assert(_sigIntRegistration is not null); - - _sigIntRegistration?.Dispose(); - _sigQuitRegistration?.Dispose(); - } - - private static void HandlePosixSignal(PosixSignalContext ctx) - { - Debug.Assert(ctx.Signal == PosixSignal.SIGINT || ctx.Signal == PosixSignal.SIGQUIT); - - ConsoleSpecialKey controlKey = ctx.Signal == PosixSignal.SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; - bool cancel = Console.HandleBreakEvent(controlKey, ctx.Cancel); - ctx.Cancel = cancel; - } - } - private sealed class ReadOnlyMemoryContentComparer : IEqualityComparer> { public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) => diff --git a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs index 405326185ebb7..ae4dc31078dfb 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs @@ -224,14 +224,5 @@ public static int WindowHeight public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs index 95cebc12c4887..a4f1241e0699d 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @@ -1224,52 +1224,5 @@ private static unsafe int WriteFileNative(IntPtr hFile, ReadOnlySpan bytes return errorCode; } } - - internal sealed class ControlCHandlerRegistrar - { - private bool _handlerRegistered; - private readonly Interop.Kernel32.ConsoleCtrlHandlerRoutine _handler; - - internal ControlCHandlerRegistrar() - { - _handler = new Interop.Kernel32.ConsoleCtrlHandlerRoutine(BreakEvent); - } - - internal void Register() - { - Debug.Assert(!_handlerRegistered); - - bool r = Interop.Kernel32.SetConsoleCtrlHandler(_handler, true); - if (!r) - { - throw Win32Marshal.GetExceptionForLastWin32Error(); - } - - _handlerRegistered = true; - } - - internal void Unregister() - { - Debug.Assert(_handlerRegistered); - - bool r = Interop.Kernel32.SetConsoleCtrlHandler(_handler, false); - if (!r) - { - throw Win32Marshal.GetExceptionForLastWin32Error(); - } - _handlerRegistered = false; - } - - private static bool BreakEvent(int controlType) - { - if (controlType != Interop.Kernel32.CTRL_C_EVENT && - controlType != Interop.Kernel32.CTRL_BREAK_EVENT) - { - return false; - } - - return Console.HandleBreakEvent(controlType == Interop.Kernel32.CTRL_C_EVENT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak); - } - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs index fa1f2d120a8c8..5279df26401d0 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs @@ -155,14 +155,5 @@ public static int WindowHeight public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 8be17302389c1..374a3f14c55fc 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -798,11 +798,17 @@ public OptionalAttribute() { } } public enum PosixSignal { + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTSTP = -10, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTTOU = -9, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTTIN = -8, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGWINCH = -7, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGCONT = -6, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGCHLD = -5, SIGTERM = -4, SIGQUIT = -3, @@ -818,6 +824,11 @@ public PosixSignalContext(System.Runtime.InteropServices.PosixSignal signal) { } public sealed partial class PosixSignalRegistration : System.IDisposable { internal PosixSignalRegistration() { } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Runtime.InteropServices.PosixSignalRegistration Create(System.Runtime.InteropServices.PosixSignal signal, System.Action handler) { throw null; } public void Dispose() { } ~PosixSignalRegistration() { } diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index 7834b952cc9a9..d7644789f28c2 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -111,4 +111,7 @@ Specified file length was too large for the file system. - \ No newline at end of file + + Cannot create '{0}' because a file or directory with the same name already exists. + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index c6ba2fbe496fd..7137155589b23 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -35,6 +35,7 @@ + @@ -52,22 +53,23 @@ - - - - - + + + + + + + + + + + - + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs index f1cb32a362b8d..e1f278fbd6f55 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs @@ -1,19 +1,47 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.Versioning; + namespace System.Runtime.InteropServices { + /// Specifies a POSIX signal number. public enum PosixSignal { + /// Hangup SIGHUP = -1, + + /// Interrupt SIGINT = -2, + + /// Quit SIGQUIT = -3, + + /// Termination SIGTERM = -4, + + /// Child stopped + [UnsupportedOSPlatform("windows")] SIGCHLD = -5, + + /// Continue if stopped + [UnsupportedOSPlatform("windows")] SIGCONT = -6, + + /// Window resized + [UnsupportedOSPlatform("windows")] SIGWINCH = -7, + + /// Terminal input for background process + [UnsupportedOSPlatform("windows")] SIGTTIN = -8, + + /// Terminal output for background process + [UnsupportedOSPlatform("windows")] SIGTTOU = -9, + + /// Stop typed at terminal + [UnsupportedOSPlatform("windows")] SIGTSTP = -10 } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs index 19ba4f9686849..cf73dd62dc70a 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs @@ -11,30 +11,16 @@ public sealed class PosixSignalContext /// /// Initializes a new instance of the class. /// - public PosixSignalContext(PosixSignal signal) - { - Signal = signal; - } + public PosixSignalContext(PosixSignal signal) => Signal = signal; /// /// Gets the signal that occurred. /// - public PosixSignal Signal - { - get; - internal set; - } + public PosixSignal Signal { get; internal set; } /// /// Gets or sets a value that indicates whether to cancel the default handling of the signal. The default is . /// - public bool Cancel - { - get; - set; - } - - internal PosixSignalContext() - { } + public bool Cancel { get; set; } } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs deleted file mode 100644 index 7c88a92cae944..0000000000000 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace System.Runtime.InteropServices -{ - public sealed class PosixSignalRegistration : IDisposable - { - private PosixSignalRegistration() { } - - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) - => throw new PlatformNotSupportedException(); - - public void Dispose() - => throw new PlatformNotSupportedException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs index 9a3144f5e8163..f01b2de013038 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs @@ -1,17 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.ComponentModel; using System.Threading; namespace System.Runtime.InteropServices { - /// - /// Handles a . - /// - public sealed class PosixSignalRegistration : IDisposable + public sealed partial class PosixSignalRegistration { private static volatile bool s_initialized; private static readonly Dictionary?>> s_registrations = new(); @@ -29,47 +24,24 @@ private PosixSignalRegistration(PosixSignal signal, int signo, Action - /// Registers a that is invoked when the occurs. - /// - /// The signal to register for. - /// The handler that gets invoked. - /// A instance that can be disposed to unregister. - /// is . - /// is out the range of expected values for the platform. - /// An error occurred while setting up the signal handling or while installing the handler for the specified signal. - /// - /// Raw values can be provided for by casting them to . - /// - /// Default handling of the signal can be canceled through . - /// For SIGTERM, SIGINT, and SIGQUIT process termination can be canceled. - /// For SIGCHLD, and SIGCONT terminal configuration can be canceled. - /// - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } + int signo = Interop.Sys.GetPlatformSignalNumber(signal); if (signo == 0) { - throw new ArgumentOutOfRangeException(nameof(signal)); + throw new PlatformNotSupportedException(); } + PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler); registration.Register(); return registration; } - /// - /// Unregister the handler. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - private unsafe void Register() { if (!s_initialized) @@ -84,6 +56,7 @@ private unsafe void Register() Interop.Sys.SetPosixSignalHandler(&OnPosixSignal); s_initialized = true; } + lock (s_registrations) { if (!s_registrations.TryGetValue(_signo, out List?>? signalRegistrations)) @@ -104,6 +77,7 @@ private unsafe void Register() signalRegistrations.Add(new WeakReference(this)); } + _registered = true; } @@ -116,6 +90,7 @@ private bool CallHandler(PosixSignalContext context) _handler(context); return true; } + return false; } } @@ -133,8 +108,9 @@ private static int OnPosixSignal(int signo, PosixSignal signal) // For terminate/interrupt signals we use a dedicated Thread // in case the ThreadPool is saturated. bool useDedicatedThread = signal == PosixSignal.SIGINT || - signal == PosixSignal.SIGQUIT || - signal == PosixSignal.SIGTERM; + signal == PosixSignal.SIGQUIT || + signal == PosixSignal.SIGTERM; + if (useDedicatedThread) { Thread handlerThread = new Thread(HandleSignal) @@ -148,8 +124,10 @@ private static int OnPosixSignal(int signo, PosixSignal signal) { ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations)); } + return 1; } + return 0; } @@ -164,6 +142,7 @@ private static int OnPosixSignal(int signo, PosixSignal signal) var registrations = new PosixSignalRegistration?[signalRegistrations.Count]; bool hasRegistrations = false; bool pruneWeakReferences = false; + for (int i = 0; i < signalRegistrations.Count; i++) { if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) @@ -210,7 +189,7 @@ private static void HandleSignal((int signo, PosixSignalRegistration?[]? registr bool handlersCalled = false; if (state.registrations != null) { - PosixSignalContext ctx = new(); + PosixSignalContext ctx = new(0); foreach (PosixSignalRegistration? registration in state.registrations) { if (registration != null) @@ -241,10 +220,7 @@ private static void HandleSignal((int signo, PosixSignalRegistration?[]? registr } while (true); } - ~PosixSignalRegistration() - => Dispose(false); - - private void Dispose(bool disposing) + public partial void Dispose() { if (_registered) { @@ -256,7 +232,7 @@ private void Dispose(bool disposing) { if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) { - if (object.ReferenceEquals(this, registration)) + if (ReferenceEquals(this, registration)) { signalRegistrations.RemoveAt(i); break; diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs new file mode 100644 index 0000000000000..4bfd2eae0be67 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public sealed partial class PosixSignalRegistration + { + private PosixSignalRegistration() { } + + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + throw new PlatformNotSupportedException(); + } + + public partial void Dispose() { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs index 7c88a92cae944..d3e5405053e56 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs @@ -1,18 +1,134 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; +using System.Collections.Generic; +using System.IO; namespace System.Runtime.InteropServices { - public sealed class PosixSignalRegistration : IDisposable + public sealed unsafe partial class PosixSignalRegistration { - private PosixSignalRegistration() { } + private static readonly HashSet s_handlers = new(); - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) - => throw new PlatformNotSupportedException(); + private Token? _token; - public void Dispose() - => throw new PlatformNotSupportedException(); + private PosixSignalRegistration(Token token) => _token = token; + + private static object SyncObj => s_handlers; + + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + lock (SyncObj) + { + switch (signal) + { + case PosixSignal.SIGINT: + case PosixSignal.SIGQUIT: + case PosixSignal.SIGTERM: + case PosixSignal.SIGHUP: + break; + + default: + throw new PlatformNotSupportedException(); + } + + if (s_handlers.Count == 0 && + !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: true)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + + var token = new Token(signal, handler); + s_handlers.Add(token); + return new PosixSignalRegistration(token); + } + } + + public partial void Dispose() + { + lock (SyncObj) + { + if (_token is Token token) + { + _token = null; + + s_handlers.Remove(token); + if (s_handlers.Count == 0 && + !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: false)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + } + } + + [UnmanagedCallersOnly] + private static Interop.BOOL HandlerRoutine(int dwCtrlType) + { + PosixSignal signal; + switch (dwCtrlType) + { + case Interop.Kernel32.CTRL_C_EVENT: + signal = PosixSignal.SIGINT; + break; + + case Interop.Kernel32.CTRL_BREAK_EVENT: + signal = PosixSignal.SIGQUIT; + break; + + case Interop.Kernel32.CTRL_SHUTDOWN_EVENT: + signal = PosixSignal.SIGTERM; + break; + + case Interop.Kernel32.CTRL_CLOSE_EVENT: + signal = PosixSignal.SIGHUP; + break; + + default: + return Interop.BOOL.FALSE; + } + + List? tokens = null; + lock (SyncObj) + { + foreach (Token token in s_handlers) + { + if (token.Signal == signal) + { + (tokens ??= new()).Add(token); + } + } + } + + if (tokens is null) + { + return Interop.BOOL.FALSE; + } + + var context = new PosixSignalContext(signal); + foreach (Token handler in tokens) + { + handler.Handler(context); + } + + return context.Cancel ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + } + + private sealed class Token + { + public Token(PosixSignal signal, Action handler) + { + Signal = signal; + Handler = handler; + } + + public PosixSignal Signal { get; } + public Action Handler { get; } + } } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs new file mode 100644 index 0000000000000..fb2c675acb8b1 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices +{ + /// Handles a . + public sealed partial class PosixSignalRegistration : IDisposable + { + /// Registers a that is invoked when the occurs. + /// The signal to register for. + /// The handler that gets invoked. + /// A instance that can be disposed to unregister the handler. + /// is . + /// is not supported by the platform. + /// An error occurred while setting up the signal handling or while installing the handler for the specified signal. + /// + /// Raw values can be provided for on Unix by casting them to . + /// Default handling of the signal can be canceled through . + /// and can be canceled on both + /// Windows and on Unix platforms; can only be canceled on Unix. + /// On Unix, terminal configuration can be canceled for and . + /// + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler); + + /// Unregister the handler. + public partial void Dispose(); + + ~PosixSignalRegistration() => Dispose(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 4bcb75b0b810c..33204d0ee7cc9 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -151,6 +151,7 @@ + @@ -184,9 +185,14 @@ - + + + + - + + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs index 3d8ca635ac20f..14dc8ff40100f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs @@ -9,14 +9,32 @@ namespace System.Tests public class PosixSignalContextTests { [Theory] - [InlineData(0)] - [InlineData(3)] - [InlineData(-1000)] - [InlineData(1000)] - public void Constructor(int value) + [InlineData(PosixSignal.SIGINT)] + [InlineData((PosixSignal)0)] + [InlineData((PosixSignal)1000)] + [InlineData((PosixSignal)(-1000))] + public void Constructor(PosixSignal value) { - var ctx = new PosixSignalContext((PosixSignal)value); - Assert.Equal(value, (int)ctx.Signal); + var ctx = new PosixSignalContext(value); + Assert.Equal(value, ctx.Signal); + Assert.False(ctx.Cancel); + } + + [Fact] + public void Cancel_Roundtrips() + { + var ctx = new PosixSignalContext(PosixSignal.SIGINT); + Assert.Equal(PosixSignal.SIGINT, ctx.Signal); + Assert.False(ctx.Cancel); + + for (int i = 0; i < 2; i++) + { + ctx.Cancel = true; + Assert.True(ctx.Cancel); + + ctx.Cancel = false; + Assert.False(ctx.Cancel); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs new file mode 100644 index 0000000000000..2202ed25744ee --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + public static IEnumerable UninstallableSignals() => Enumerable.Empty(); + + public static IEnumerable SupportedSignals() => Enumerable.Empty(); + + public static IEnumerable UnsupportedSignals() + { + foreach (PosixSignal signal in Enum.GetValues()) + { + yield return new object[] { signal }; + } + + yield return new object[] { 0 }; + yield return new object[] { 3 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs index b006af50150d3..1716a3ba24f7f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -2,54 +2,42 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; -using System.IO; using System.Threading; -using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Microsoft.DotNet.RemoteExecutor; +using System.Collections.Generic; namespace System.Tests { - public class PosixSignalRegistrationTests + public partial class PosixSignalRegistrationTests { - private static TimeSpan Timeout => TimeSpan.FromSeconds(30); - - [Fact] - public void HandlerNullThrows() + public static IEnumerable UninstallableSignals() { - Assert.Throws(() => PosixSignalRegistration.Create(PosixSignal.SIGCONT, null)); + yield return new object[] { (PosixSignal)9 }; } - [Theory] - [InlineData(0)] - [InlineData(-1000)] - [InlineData(1000)] - public void InvalidSignalValueThrows(int value) + public static IEnumerable SupportedSignals() { - Assert.Throws(() => PosixSignalRegistration.Create((PosixSignal)value, ctx => { })); + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; } - [Theory] - [InlineData((PosixSignal)9)] // SIGKILL - public void UninstallableSignalsThrow(PosixSignal signal) + public static IEnumerable UnsupportedSignals() { - Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + yield return new object[] { 0 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; } - [Theory] - [MemberData(nameof(PosixSignalValues))] - public void CanRegisterForKnownValues(PosixSignal signal) - { - using var _ = PosixSignalRegistration.Create(signal, ctx => { }); - } - - [Theory] - [MemberData(nameof(PosixSignalValues))] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [MemberData(nameof(SupportedSignals))] public void SignalHandlerCalledForKnownSignals(PosixSignal s) { - RemoteExecutor.Invoke((signalStr) => { + RemoteExecutor.Invoke(signalStr => + { PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); using var _ = PosixSignalRegistration.Create(signal, ctx => { @@ -60,18 +48,21 @@ public void SignalHandlerCalledForKnownSignals(PosixSignal s) semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); }, s.ToString()).Dispose(); } - [Theory] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(PosixSignalAsRawValues))] public void SignalHandlerCalledForRawSignals(PosixSignal s) { - RemoteExecutor.Invoke((signalStr) => { + RemoteExecutor.Invoke((signalStr) => + { PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); using var _ = PosixSignalRegistration.Create(signal, ctx => { @@ -82,8 +73,9 @@ public void SignalHandlerCalledForRawSignals(PosixSignal s) semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); }, s.ToString()).Dispose(); } @@ -105,8 +97,9 @@ public void SignalHandlerWorksForSecondRegistration() semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); } } @@ -116,11 +109,10 @@ public void SignalHandlerNotCalledWhenDisposed() { PosixSignal signal = PosixSignal.SIGCONT; - using var registration = PosixSignalRegistration.Create(signal, ctx => + PosixSignalRegistration.Create(signal, ctx => { Assert.False(true, "Signal handler was called."); - }); - registration.Dispose(); + }).Dispose(); kill(signal); Thread.Sleep(100); @@ -179,7 +171,7 @@ public void SignalCanCancelTermination(PosixSignal signal, bool cancel, int expe kill(signalArg); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); // Give the default signal handler a chance to run. @@ -190,19 +182,6 @@ public void SignalCanCancelTermination(PosixSignal signal, bool cancel, int expe new RemoteInvokeOptions() { ExpectedExitCode = expectedExitCode, TimeOut = 10 * 60 * 1000 }).Dispose(); } - public static TheoryData PosixSignalValues - { - get - { - var data = new TheoryData(); - foreach (var value in Enum.GetValues(typeof(PosixSignal))) - { - data.Add((PosixSignal)value); - } - return data; - } - } - public static TheoryData PosixSignalAsRawValues { get @@ -230,7 +209,6 @@ private static void kill(PosixSignal sig) } [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")] - [SuppressGCTransition] private static extern int GetPlatformSignalNumber(PosixSignal signal); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs new file mode 100644 index 0000000000000..ad5099bb85057 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + public static IEnumerable UninstallableSignals() => Enumerable.Empty(); + + public static IEnumerable SupportedSignals() => SupportedPosixSignals.Select(p => new object[] { p }); + + public static IEnumerable UnsupportedSignals() + { + foreach (PosixSignal signal in Enum.GetValues().Except(SupportedPosixSignals)) + { + yield return new object[] { signal }; + } + + yield return new object[] { 0 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; + } + + private static IEnumerable SupportedPosixSignals => new[] { PosixSignal.SIGINT, PosixSignal.SIGQUIT, PosixSignal.SIGTERM, PosixSignal.SIGHUP }; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs new file mode 100644 index 0000000000000..599d1adc6c6d0 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using Xunit; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + private static TimeSpan SuccessTimeout => TimeSpan.FromSeconds(30); + + [Fact] + public void Create_NullHandler_Throws() + { + AssertExtensions.Throws("handler", () => PosixSignalRegistration.Create(PosixSignal.SIGCONT, null)); + } + + [Theory] + [MemberData(nameof(UnsupportedSignals))] + public void Create_InvalidSignal_Throws(PosixSignal signal) + { + Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + } + + [Theory] + [MemberData(nameof(UninstallableSignals))] + public void Create_UninstallableSignal_Throws(PosixSignal signal) + { + Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + } + + [Theory] + [MemberData(nameof(SupportedSignals))] + public void Create_ValidSignal_Success(PosixSignal signal) + { + PosixSignalRegistration.Create(signal, ctx => { }).Dispose(); + } + + [Theory] + [MemberData(nameof(SupportedSignals))] + public void Dispose_Idempotent(PosixSignal signal) + { + PosixSignalRegistration registration = PosixSignalRegistration.Create(signal, ctx => { }); + registration.Dispose(); + registration.Dispose(); + } + + [Fact] + public void Create_RegisterForMultipleSignalsMultipletimes_Success() + { + var registrations = new List(); + for (int i = 0; i < 3; i++) + { + foreach (object[] signal in SupportedSignals()) + { + registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + } + + foreach (object[] signal in SupportedSignals()) + { + registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + } + + foreach (PosixSignalRegistration registration in registrations) + { + registration.Dispose(); + } + } + } + } +} From dcce0f56e10f5ac9539354b049341a2d7c0cdebf Mon Sep 17 00:00:00 2001 From: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> Date: Fri, 9 Jul 2021 04:17:31 -0700 Subject: [PATCH 18/72] Add symbolic link APIs (#54253) Co-authored-by: David Cantu Co-authored-by: carlossanlop Co-authored-by: carlossanlop --- .../Unix/Interop.DefaultPathBufferSize.cs | 9 + .../Unix/System.Native/Interop.GetCwd.cs | 8 +- .../Unix/System.Native/Interop.ReadLink.cs | 30 +- .../Unix/System.Native/Interop.Stat.Span.cs | 8 +- .../Unix/System.Native/Interop.SymLink.cs | 14 + .../src/Interop/Windows/Interop.Errors.cs | 1 + .../Kernel32/Interop.CreateSymbolicLink.cs | 70 +++ .../Kernel32/Interop.DeviceIoControl.cs | 26 + .../Kernel32/Interop.FileOperations.cs | 2 + .../Interop.GetFinalPathNameByHandle.cs | 23 + .../Kernel32/Interop.REPARSE_DATA_BUFFER.cs | 37 ++ .../IO/FileSystem.Attributes.Windows.cs | 36 +- .../System/IO/FileCleanupTestBase.cs | 58 ++ .../src/Microsoft.IO.Redist.csproj | 12 +- .../src/Resources/Strings.resx | 3 + .../Native/Unix/System.Native/entrypoints.c | 1 + .../Native/Unix/System.Native/pal_io.c | 7 + .../Native/Unix/System.Native/pal_io.h | 9 +- .../src/System.Diagnostics.Process.csproj | 4 + .../src/System.IO.FileSystem.Watcher.csproj | 2 + .../tests/Utility/FileSystemWatcherTest.cs | 62 -- .../tests/Base/BaseSymbolicLinks.cs | 18 - .../BaseSymbolicLinks.FileSystem.cs | 531 ++++++++++++++++++ .../BaseSymbolicLinks.FileSystemInfo.cs | 101 ++++ .../SymbolicLinks/BaseSymbolicLinks.Unix.cs | 19 + .../BaseSymbolicLinks.Windows.cs | 66 +++ .../Base/SymbolicLinks/BaseSymbolicLinks.cs | 34 ++ .../tests/Directory/SymbolicLinks.cs | 67 ++- .../tests/DirectoryInfo/SymbolicLinks.cs | 64 ++- .../tests/File/SymbolicLinks.cs | 55 ++ .../tests/FileInfo/SymbolicLinks.cs | 51 ++ .../tests/FileSystemTest.cs | 26 - ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 6 + .../tests/System.IO.FileSystem.Tests.csproj | 15 +- .../src/System.Net.Ping.csproj | 4 + .../System.Net.Ping.Functional.Tests.csproj | 4 + .../src/Resources/Strings.resx | 6 + .../System.Private.CoreLib.Shared.projitems | 19 + .../src/System/IO/Directory.cs | 42 ++ .../src/System/IO/File.cs | 40 ++ .../src/System/IO/FileSystem.Unix.cs | 91 +++ .../src/System/IO/FileSystem.Windows.cs | 241 +++++++- .../src/System/IO/FileSystem.cs | 29 + .../src/System/IO/FileSystemInfo.Unix.cs | 8 +- .../src/System/IO/FileSystemInfo.Windows.cs | 15 +- .../src/System/IO/FileSystemInfo.cs | 62 ++ .../src/System/IO/Path.cs | 2 +- .../src/System/IO/PathInternal.cs | 8 + .../System.Runtime/ref/System.Runtime.cs | 7 + ...urity.Cryptography.X509Certificates.csproj | 4 + 50 files changed, 1883 insertions(+), 174 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs delete mode 100644 src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs diff --git a/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs new file mode 100644 index 0000000000000..d9807b427bf26 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths + // without putting too much pressure on the stack. + internal const int DefaultPathBufferSize = 256; +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs index 1faef8cc0be8d..78da5a667310f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs @@ -14,18 +14,16 @@ internal static partial class Sys internal static unsafe string GetCwd() { - const int StackLimit = 256; - // First try to get the path into a buffer on the stack - byte* stackBuf = stackalloc byte[StackLimit]; - string? result = GetCwdHelper(stackBuf, StackLimit); + byte* stackBuf = stackalloc byte[DefaultPathBufferSize]; + string? result = GetCwdHelper(stackBuf, DefaultPathBufferSize); if (result != null) { return result; } // If that was too small, try increasing large buffer sizes - int bufferSize = StackLimit; + int bufferSize = DefaultPathBufferSize; while (true) { checked { bufferSize *= 2; } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs index 8f0f6a15fed95..94f37d4ccc3f8 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using System.Buffers; using System.Text; +using System; internal static partial class Interop { @@ -20,24 +21,31 @@ internal static partial class Sys /// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error. /// [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)] - private static extern int ReadLink(string path, byte[] buffer, int bufferSize); + private static extern int ReadLink(ref byte path, byte[] buffer, int bufferSize); /// /// Takes a path to a symbolic link and returns the link target path. /// - /// The path to the symlink - /// - /// Returns the link to the target path on success; and null otherwise. - /// - public static string? ReadLink(string path) + /// The path to the symlink. + /// Returns the link to the target path on success; and null otherwise. + internal static string? ReadLink(ReadOnlySpan path) { - int bufferSize = 256; + int outputBufferSize = 1024; + + // Use an initial buffer size that prevents disposing and renting + // a second time when calling ConvertAndTerminateString. + using var converter = new ValueUtf8Converter(stackalloc byte[1024]); + while (true) { - byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + byte[] buffer = ArrayPool.Shared.Rent(outputBufferSize); try { - int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length); + int resultLength = Interop.Sys.ReadLink( + ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), + buffer, + buffer.Length); + if (resultLength < 0) { // error @@ -54,8 +62,8 @@ internal static partial class Sys ArrayPool.Shared.Return(buffer); } - // buffer was too small, loop around again and try with a larger buffer. - bufferSize *= 2; + // Output buffer was too small, loop around again and try with a larger buffer. + outputBufferSize = buffer.Length * 2; } } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs index 3c638cb60aa52..85028fd0fd088 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs @@ -9,16 +9,12 @@ internal static partial class Interop { internal static partial class Sys { - // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths - // without putting too much pressure on the stack. - private const int StackBufferSize = 256; - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)] internal static extern int Stat(ref byte path, out FileStatus output); internal static int Stat(ReadOnlySpan path, out FileStatus output) { - var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]); + var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]); int result = Stat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output); converter.Dispose(); return result; @@ -29,7 +25,7 @@ internal static int Stat(ReadOnlySpan path, out FileStatus output) internal static int LStat(ReadOnlySpan path, out FileStatus output) { - var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]); + var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]); int result = LStat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output); converter.Dispose(); return result; diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs new file mode 100644 index 0000000000000..922ecd5bc6625 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SymLink", SetLastError = true)] + internal static extern int SymLink(string target, string linkPath); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs index 338706ea8491b..d5f6d1637507f 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs @@ -91,5 +91,6 @@ internal static partial class Errors internal const int ERROR_EVENTLOG_FILE_CHANGED = 0x5DF; internal const int ERROR_TRUSTED_RELATIONSHIP_FAILURE = 0x6FD; internal const int ERROR_RESOURCE_LANG_NOT_FOUND = 0x717; + internal const int ERROR_NOT_A_REPARSE_POINT = 0x1126; } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs new file mode 100644 index 0000000000000..9ecd41c46bd6b --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + /// + /// The link target is a directory. + /// + internal const int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1; + + /// + /// Allows creation of symbolic links from a process that is not elevated. Requires Windows 10 Insiders build 14972 or later. + /// Developer Mode must first be enabled on the machine before this option will function. + /// + internal const int SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2; + + [DllImport(Libraries.Kernel32, EntryPoint = "CreateSymbolicLinkW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + private static extern bool CreateSymbolicLinkPrivate(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); + + /// + /// Creates a symbolic link. + /// + /// The symbolic link to be created. + /// The name of the target for the symbolic link to be created. + /// If it has a device name associated with it, the link is treated as an absolute link; otherwise, the link is treated as a relative link. + /// if the link target is a directory; otherwise. + internal static void CreateSymbolicLink(string symlinkFileName, string targetFileName, bool isDirectory) + { + string originalPath = symlinkFileName; + symlinkFileName = PathInternal.EnsureExtendedPrefixIfNeeded(symlinkFileName); + targetFileName = PathInternal.EnsureExtendedPrefixIfNeeded(targetFileName); + + int flags = 0; + + bool isAtLeastWin10Build14972 = + Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 14972 || + Environment.OSVersion.Version.Major >= 11; + + if (isAtLeastWin10Build14972) + { + flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + if (isDirectory) + { + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; + } + + bool success = CreateSymbolicLinkPrivate(symlinkFileName, targetFileName, flags); + + int error; + if (!success) + { + throw Win32Marshal.GetExceptionForLastWin32Error(originalPath); + } + // In older versions we need to check GetLastWin32Error regardless of the return value of CreateSymbolicLink, + // e.g: if the user doesn't have enough privileges to create a symlink the method returns success which we can consider as a silent failure. + else if (!isAtLeastWin10Build14972 && (error = Marshal.GetLastWin32Error()) != 0) + { + throw Win32Marshal.GetExceptionForWin32Error(error, originalPath); + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs new file mode 100644 index 0000000000000..be8def215178f --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point + internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8; + + [DllImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern bool DeviceIoControl( + SafeHandle hDevice, + uint dwIoControlCode, + IntPtr lpInBuffer, + uint nInBufferSize, + byte[] lpOutBuffer, + uint nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs index f2a3872299d2c..cc4896c1c52e4 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs @@ -9,6 +9,7 @@ internal static partial class IOReparseOptions { internal const uint IO_REPARSE_TAG_FILE_PLACEHOLDER = 0x80000015; internal const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; + internal const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C; } internal static partial class FileOperations @@ -18,6 +19,7 @@ internal static partial class FileOperations internal const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; internal const int FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000; + internal const int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; internal const int FILE_FLAG_OVERLAPPED = 0x40000000; internal const int FILE_LIST_DIRECTORY = 0x0001; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs new file mode 100644 index 0000000000000..756b1bbd72db1 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + internal const uint FILE_NAME_NORMALIZED = 0x0; + + // https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew (kernel32) + [DllImport(Libraries.Kernel32, EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static unsafe extern uint GetFinalPathNameByHandle( + SafeFileHandle hFile, + char* lpszFilePath, + uint cchFilePath, + uint dwFlags); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs new file mode 100644 index 0000000000000..3bcb9162d57bf --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // https://docs.microsoft.com/windows-hardware/drivers/ifs/fsctl-get-reparse-point + internal const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; + + internal const uint SYMLINK_FLAG_RELATIVE = 1; + + // https://msdn.microsoft.com/library/windows/hardware/ff552012.aspx + // We don't need all the struct fields; omitting the rest. + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct REPARSE_DATA_BUFFER + { + internal uint ReparseTag; + internal ushort ReparseDataLength; + internal ushort Reserved; + internal SymbolicLinkReparseBuffer ReparseBufferSymbolicLink; + + [StructLayout(LayoutKind.Sequential)] + internal struct SymbolicLinkReparseBuffer + { + internal ushort SubstituteNameOffset; + internal ushort SubstituteNameLength; + internal ushort PrintNameOffset; + internal ushort PrintNameLength; + internal uint Flags; + } + } + } +} diff --git a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs index 60e7a0aa466f1..ad087304b4e5a 100644 --- a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs +++ b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs @@ -64,19 +64,7 @@ internal static int FillAttributeInfo(string? path, ref Interop.Kernel32.WIN32_F { errorCode = Marshal.GetLastWin32Error(); - if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND - && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND - && errorCode != Interop.Errors.ERROR_NOT_READY - && errorCode != Interop.Errors.ERROR_INVALID_NAME - && errorCode != Interop.Errors.ERROR_BAD_PATHNAME - && errorCode != Interop.Errors.ERROR_BAD_NETPATH - && errorCode != Interop.Errors.ERROR_BAD_NET_NAME - && errorCode != Interop.Errors.ERROR_INVALID_PARAMETER - && errorCode != Interop.Errors.ERROR_NETWORK_UNREACHABLE - && errorCode != Interop.Errors.ERROR_NETWORK_ACCESS_DENIED - && errorCode != Interop.Errors.ERROR_INVALID_HANDLE // eg from \\.\CON - && errorCode != Interop.Errors.ERROR_FILENAME_EXCED_RANGE // Path is too long - ) + if (!IsPathUnreachableError(errorCode)) { // Assert so we can track down other cases (if any) to add to our test suite Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION || errorCode == Interop.Errors.ERROR_SEM_TIMEOUT, @@ -127,5 +115,27 @@ internal static int FillAttributeInfo(string? path, ref Interop.Kernel32.WIN32_F return errorCode; } + + internal static bool IsPathUnreachableError(int errorCode) + { + switch (errorCode) + { + case Interop.Errors.ERROR_FILE_NOT_FOUND: + case Interop.Errors.ERROR_PATH_NOT_FOUND: + case Interop.Errors.ERROR_NOT_READY: + case Interop.Errors.ERROR_INVALID_NAME: + case Interop.Errors.ERROR_BAD_PATHNAME: + case Interop.Errors.ERROR_BAD_NETPATH: + case Interop.Errors.ERROR_BAD_NET_NAME: + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NETWORK_UNREACHABLE: + case Interop.Errors.ERROR_NETWORK_ACCESS_DENIED: + case Interop.Errors.ERROR_INVALID_HANDLE: // eg from \\.\CON + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: // Path is too long + return true; + default: + return false; + } + } } } diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index 02ffa607c94aa..a45aab12165a5 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -127,5 +127,63 @@ private string GenerateTestFileName(int? index, string memberName, int lineNumbe lineNumber, index.GetValueOrDefault(), Guid.NewGuid().ToString("N").Substring(0, 8)); // randomness to avoid collisions between derived test classes using same base method concurrently + + /// + /// In some cases (such as when running without elevated privileges), + /// the symbolic link may fail to create. Only run this test if it creates + /// links successfully. + /// + protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; + + private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => + { + bool success = true; + + // Verify file symlink creation + string path = Path.GetTempFileName(); + string linkPath = path + ".link"; + success = CreateSymLink(path, linkPath, isDirectory: false); + try { File.Delete(path); } catch { } + try { File.Delete(linkPath); } catch { } + + // Verify directory symlink creation + path = Path.GetTempFileName(); + linkPath = path + ".link"; + success = success && CreateSymLink(path, linkPath, isDirectory: true); + try { Directory.Delete(path); } catch { } + try { Directory.Delete(linkPath); } catch { } + + return success; + }); + + protected static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory) + { +#if NETFRAMEWORK + bool isWindows = true; +#else + if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsBrowser()) // OSes that don't support Process.Start() + { + return false; + } + bool isWindows = OperatingSystem.IsWindows(); +#endif + Process symLinkProcess = new Process(); + if (isWindows) + { + symLinkProcess.StartInfo.FileName = "cmd"; + symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath)); + } + else + { + symLinkProcess.StartInfo.FileName = "/bin/ln"; + symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath)); + } + symLinkProcess.StartInfo.RedirectStandardOutput = true; + symLinkProcess.Start(); + + symLinkProcess.WaitForExit(); + return (0 == symLinkProcess.ExitCode); + } + } } diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj index db9472c1432f3..f22780b1a5e6e 100644 --- a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj +++ b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj @@ -1,4 +1,4 @@ - + net472 $(DefineConstants);MS_IO_REDIST @@ -34,6 +34,8 @@ Link="Microsoft\IO\File.cs" /> + + + + + The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. + + The link's file system entry type is inconsistent with that of its target: {0} + Could not find a part of the path. diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index b1b5a92e5f35b..f39360810b57e 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -81,6 +81,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_Access) DllImportEntry(SystemNative_LSeek) DllImportEntry(SystemNative_Link) + DllImportEntry(SystemNative_SymLink) DllImportEntry(SystemNative_MksTemps) DllImportEntry(SystemNative_MMap) DllImportEntry(SystemNative_MUnmap) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index d7eb6c4ab23ac..9e02aa7c2e65d 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -715,6 +715,13 @@ int32_t SystemNative_Link(const char* source, const char* linkTarget) return result; } +int32_t SystemNative_SymLink(const char* target, const char* linkPath) +{ + int32_t result; + while ((result = symlink(target, linkPath)) < 0 && errno == EINTR); + return result; +} + intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength) { intptr_t result; diff --git a/src/libraries/Native/Unix/System.Native/pal_io.h b/src/libraries/Native/Unix/System.Native/pal_io.h index 1dc70387ad5ea..6461881a91eb1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.h +++ b/src/libraries/Native/Unix/System.Native/pal_io.h @@ -527,12 +527,19 @@ PALEXPORT int32_t SystemNative_Access(const char* path, int32_t mode); PALEXPORT int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, int32_t whence); /** - * Creates a hard-link at link pointing to source. + * Creates a hard-link at linkTarget pointing to source. * * Returns 0 on success; otherwise, returns -1 and errno is set. */ PALEXPORT int32_t SystemNative_Link(const char* source, const char* linkTarget); +/** + * Creates a symbolic link at linkPath pointing to target. + * + * Returns 0 on success; otherwise, returns -1 and errno is set. + */ +PALEXPORT int32_t SystemNative_SymLink(const char* target, const char* linkPath); + /** * Creates a file name that adheres to the specified template, creates the file on disk with * 0600 permissions, and returns an open r/w File Descriptor on the file. diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 424a7a87b31c6..882b1beb85ffc 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -233,6 +233,8 @@ Link="Common\Interop\Unix\Interop.Errors.cs" /> + + + - /// In some cases (such as when running without elevated privileges), - /// the symbolic link may fail to create. Only run this test if it creates - /// links successfully. - /// - protected static bool CanCreateSymbolicLinks - { - get - { - bool success = true; - - // Verify file symlink creation - string path = Path.GetTempFileName(); - string linkPath = path + ".link"; - success = CreateSymLink(path, linkPath, isDirectory: false); - try { File.Delete(path); } catch { } - try { File.Delete(linkPath); } catch { } - - // Verify directory symlink creation - path = Path.GetTempFileName(); - linkPath = path + ".link"; - success = success && CreateSymLink(path, linkPath, isDirectory: true); - try { Directory.Delete(path); } catch { } - try { Directory.Delete(linkPath); } catch { } - - return success; - } - } - - public static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory) - { - if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst()) // OSes that don't support Process.Start() - { - return false; - } - - Process symLinkProcess = new Process(); - if (OperatingSystem.IsWindows()) - { - symLinkProcess.StartInfo.FileName = "cmd"; - symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath)); - } - else - { - symLinkProcess.StartInfo.FileName = "/bin/ln"; - symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath)); - } - symLinkProcess.StartInfo.RedirectStandardOutput = true; - symLinkProcess.Start(); - - if (symLinkProcess != null) - { - symLinkProcess.WaitForExit(); - return (0 == symLinkProcess.ExitCode); - } - else - { - return false; - } - } - - public static IEnumerable FilterTypes() { foreach (NotifyFilters filter in Enum.GetValues(typeof(NotifyFilters))) diff --git a/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs deleted file mode 100644 index caa5ae9c3e11d..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -namespace System.IO.Tests -{ - public abstract class BaseSymbolicLinks : FileSystemTest - { - protected DirectoryInfo CreateDirectoryContainingSelfReferencingSymbolicLink() - { - DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); - string pathToLink = Path.Join(testDirectory.FullName, GetTestFileName()); - Assert.True(MountHelper.CreateSymbolicLink(pathToLink, pathToLink, isDirectory: true)); // Create a symlink cycle - return testDirectory; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs new file mode 100644 index 0000000000000..6f9a1ace40045 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs @@ -0,0 +1,531 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.IO.Tests +{ + // Contains test methods that can be used for FileInfo, DirectoryInfo, File or Directory. + public abstract class BaseSymbolicLinks_FileSystem : BaseSymbolicLinks + { + protected abstract bool IsDirectoryTest { get; } + + /// Creates a new file or directory depending on the implementing class. + /// If createOpposite is true, creates a directory if the implementing class is for File or FileInfo, or + /// creates a file if the implementing class is for Directory or DirectoryInfo. + protected abstract void CreateFileOrDirectory(string path, bool createOpposite = false); + + protected abstract void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo); + + protected abstract void AssertLinkExists(FileSystemInfo linkInfo); + + /// Calls the actual public API for creating a symbolic link. + protected abstract FileSystemInfo CreateSymbolicLink(string path, string pathToTarget); + + private void CreateSymbolicLink_Opposite(string path, string pathToTarget) + { + if (IsDirectoryTest) + { + File.CreateSymbolicLink(path, pathToTarget); + } + else + { + Directory.CreateSymbolicLink(path, pathToTarget); + } + } + + /// Calls the actual public API for resolving the symbolic link target. + protected abstract FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget); + + [Fact] + public void CreateSymbolicLink_NullPathToTarget() + { + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), pathToTarget: null)); + } + + [Theory] + [InlineData("")] + [InlineData("\0")] + public void CreateSymbolicLink_InvalidPathToTarget(string pathToTarget) + { + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), pathToTarget)); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_TargetExists() + { + // /path/to/link -> /path/to/existingtarget + + string linkPath = GetRandomLinkPath(); + string existentTarget = GetRandomFileName(); + string targetPath = Path.Join(Path.GetDirectoryName(linkPath), existentTarget); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_TargetExists_WithRedundantSegments() + { + // /path/to/link -> /path/to/../to/existingtarget + + string linkPath = GetRandomLinkPath(); + string fileName = GetRandomFileName(); + string dirPath = Path.GetDirectoryName(linkPath); + string dirName = Path.GetFileName(dirPath); + string targetPath = Path.Join(dirPath, fileName); + string existentTarget = Path.Join("..", dirName, fileName); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_TargetExists() + { + // /path/to/link -> /path/to/existingtarget + string linkPath = GetRandomLinkPath(); + string targetPath = GetRandomFilePath(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: targetPath, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_TargetExists_WithRedundantSegments() + { + // /path/to/link -> /path/to/../to/existingtarget + + string linkPath = GetRandomLinkPath(); + string fileName = GetRandomFileName(); + string dirPath = Path.GetDirectoryName(linkPath); + string dirName = Path.GetFileName(dirPath); + string targetPath = Path.Join(dirPath, fileName); + string existentTarget = Path.Join(dirPath, "..", dirName, fileName); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_NonExistentTarget() + { + // /path/to/link -> /path/to/nonexistenttarget + + string linkPath = GetRandomLinkPath(); + string nonExistentTarget = GetRandomFileName(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: nonExistentTarget, + targetPath: null); // do not create target + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_NonExistentTarget() + { + // /path/to/link -> /path/to/nonexistenttarget + + string linkPath = GetRandomLinkPath(); + string nonExistentTarget = GetRandomFilePath(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: nonExistentTarget, + targetPath: null); // do not create target + } + + protected void ResolveLinkTarget_Throws_NotExists_Internal() where T : Exception + { + string path = GetRandomFilePath(); + Assert.Throws(() => ResolveLinkTarget(path, returnFinalTarget: false)); + Assert.Throws(() => ResolveLinkTarget(path, returnFinalTarget: true)); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ResolveLinkTarget_ReturnsNull_NotALink(bool returnFinalTarget) + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); + Assert.Null(ResolveLinkTarget(path, returnFinalTarget)); + } + + [Theory] + [MemberData(nameof(ResolveLinkTarget_PathToTarget_Data))] + public void ResolveLinkTarget_Succeeds(string pathToTarget, bool returnFinalTarget) + { + string linkPath = GetRandomLinkPath(); + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, pathToTarget); + AssertLinkExists(linkInfo); + AssertIsCorrectTypeAndDirectoryAttribute(linkInfo); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + FileSystemInfo targetInfo = ResolveLinkTarget(linkPath, returnFinalTarget); + Assert.NotNull(targetInfo); + Assert.False(targetInfo.Exists); + + string expectedTargetFullName = Path.IsPathFullyQualified(pathToTarget) ? + pathToTarget : Path.GetFullPath(Path.Join(Path.GetDirectoryName(linkPath), pathToTarget)); + + Assert.Equal(expectedTargetFullName, targetInfo.FullName); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ResolveLinkTarget_FileSystemEntryExistsButIsNotALink(bool returnFinalTarget) + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); // entry exists as a normal file, not as a link + + FileSystemInfo target = ResolveLinkTarget(path, returnFinalTarget); + Assert.Null(target); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Absolute() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: link2Path, + link2Path: link2Path, + link2Target: filePath, + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Absolute_WithRedundantSegments() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string dirPath = Path.GetDirectoryName(filePath); + string dirName = Path.GetFileName(dirPath); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: Path.Join(dirPath, "..", dirName, link2FileName), + link2Path: link2Path, + link2Target: Path.Join(dirPath, "..", dirName, fileName), + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Relative() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: link2FileName, + link2Path: link2Path, + link2Target: fileName, + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Relative_WithRedundantSegments() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string dirPath = Path.GetDirectoryName(filePath); + string dirName = Path.GetFileName(dirPath); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: Path.Join("..", dirName, link2FileName), + link2Path: link2Path, + link2Target: Path.Join("..", dirName, fileName), + filePath: filePath); + } + + [Theory] + [InlineData(1, false)] + [InlineData(10, false)] + [InlineData(20, false)] + [InlineData(1, true)] + [InlineData(10, true)] + [InlineData(20, true)] + public void ResolveLinkTarget_ReturnFinalTarget_ChainOfLinks_Succeeds(int length, bool relative) + { + string target = GetRandomFilePath(); + CreateFileOrDirectory(target); + + string tail = CreateChainOfLinks(target, length, relative); + FileSystemInfo targetInfo = ResolveLinkTarget(tail, returnFinalTarget: true); + Assert.Equal(target, targetInfo.FullName); + } + + [Theory] + // 100 is way beyond the limit (63 in Windows and 40 in Unix), we just want to make sure that a nice exception is thrown when its exceeded. + // We also don't want to test for a very precise limit given that it is very inconsistent across Windows versions. + [InlineData(100, false)] + [InlineData(100, true)] + public void ResolveLinkTarget_ReturnFinalTarget_ChainOfLinks_ExceedsLimit_Throws(int length, bool relative) + { + string target = GetRandomFilePath(); + CreateFileOrDirectory(target); + + string tail = CreateChainOfLinks(target, length, relative); + Assert.Throws(() => ResolveLinkTarget(tail, returnFinalTarget: true)); + } + + private string CreateChainOfLinks(string target, int length, bool relative) + { + string previousPath = target; + + for (int i = 0; i < length; i++) + { + string currentLinkPath = GetRandomLinkPath(); + CreateSymbolicLink(currentLinkPath, relative ? Path.GetFileName(previousPath) : previousPath); + previousPath = currentLinkPath; + } + + return previousPath; + } + + [Fact] + public void DetectSymbolicLinkCycle() + { + // link1 -> link2 -> link1 (cycle) + + string link2Path = GetRandomFilePath(); + string link1Path = GetRandomFilePath(); + + FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link2Path); + FileSystemInfo link2Info = CreateSymbolicLink(link2Path, link1Path); + + // Can get targets without following symlinks + FileSystemInfo link1Target = ResolveLinkTarget(link1Path, returnFinalTarget: false); + FileSystemInfo link2Target = ResolveLinkTarget(link2Path, returnFinalTarget: false); + + // Cannot get target when following symlinks + Assert.Throws(() => ResolveLinkTarget(link1Path, returnFinalTarget: true)); + Assert.Throws(() => ResolveLinkTarget(link2Path, returnFinalTarget: true)); + } + + [Fact] + public void DetectLinkReferenceToSelf() + { + // link -> link (reference to itself) + + string linkPath = GetRandomFilePath(); + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, linkPath); + + // Can get target without following symlinks + FileSystemInfo linkTarget = ResolveLinkTarget(linkPath, returnFinalTarget: false); + + // Cannot get target when following symlinks + Assert.Throws(() => ResolveLinkTarget(linkPath, returnFinalTarget: true)); + } + + [Fact] + public void CreateSymbolicLink_WrongTargetType_Throws() + { + // dirLink -> file + // fileLink -> dir + + string targetPath = GetRandomFilePath(); + CreateFileOrDirectory(targetPath, createOpposite: true); // The underlying file system entry needs to be different + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), targetPath)); + } + + [Fact] + public void CreateSymbolicLink_WrongTargetType_Indirect_Throws() + { + // link-2 (dir) -> link-1 (file) -> file + // link-2 (file) -> link-1 (dir) -> dir + string targetPath = GetRandomFilePath(); + string firstLinkPath = GetRandomFilePath(); + string secondLinkPath = GetRandomFilePath(); + + CreateFileOrDirectory(targetPath, createOpposite: true); + CreateSymbolicLink_Opposite(firstLinkPath, targetPath); + + Assert.Throws(() => CreateSymbolicLink(secondLinkPath, firstLinkPath)); + } + + [Fact] + public void CreateSymbolicLink_CorrectTargetType_Indirect_Succeeds() + { + // link-2 (file) -> link-1 (file) -> file + // link-2 (dir) -> link-1 (dir) -> dir + string targetPath = GetRandomFilePath(); + string firstLinkPath = GetRandomFilePath(); + string secondLinkPath = GetRandomFilePath(); + + CreateFileOrDirectory(targetPath, createOpposite: false); + CreateSymbolicLink(firstLinkPath, targetPath); + + FileSystemInfo secondLinkInfo = CreateSymbolicLink(secondLinkPath, firstLinkPath); + Assert.Equal(firstLinkPath, secondLinkInfo.LinkTarget); + Assert.Equal(targetPath, secondLinkInfo.ResolveLinkTarget(true).FullName); + } + + private void VerifySymbolicLinkAndResolvedTarget(string linkPath, string expectedLinkTarget, string targetPath = null) + { + // linkPath -> expectedLinkTarget (created in targetPath if not null) + + if (targetPath != null) + { + CreateFileOrDirectory(targetPath); + } + + FileSystemInfo link = CreateSymbolicLink(linkPath, expectedLinkTarget); + if (targetPath == null) + { + // Behavior different between files and directories when target does not exist + AssertLinkExists(link); + } + else + { + Assert.True(link.Exists); // The target file or directory was created above, so we report Exists of the target for both + } + + FileSystemInfo target = ResolveLinkTarget(linkPath, returnFinalTarget: false); + AssertIsCorrectTypeAndDirectoryAttribute(target); + Assert.True(Path.IsPathFullyQualified(target.FullName)); + } + + /// + /// Creates and Resolves a chain of links. + /// link1 -> link2 -> file + /// + private void ResolveLinkTarget_ReturnFinalTarget(string link1Path, string link1Target, string link2Path, string link2Target, string filePath) + { + Assert.True(Path.IsPathFullyQualified(link1Path)); + Assert.True(Path.IsPathFullyQualified(link2Path)); + Assert.True(Path.IsPathFullyQualified(filePath)); + + CreateFileOrDirectory(filePath); + + // link2 to file + FileSystemInfo link2Info = CreateSymbolicLink(link2Path, link2Target); + Assert.True(link2Info.Exists); + Assert.True(link2Info.Attributes.HasFlag(FileAttributes.ReparsePoint)); + AssertIsCorrectTypeAndDirectoryAttribute(link2Info); + Assert.Equal(link2Target, link2Info.LinkTarget); + + // link1 to link2 + FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link1Target); + Assert.True(link1Info.Exists); + Assert.True(link1Info.Attributes.HasFlag(FileAttributes.ReparsePoint)); + AssertIsCorrectTypeAndDirectoryAttribute(link1Info); + Assert.Equal(link1Target, link1Info.LinkTarget); + + // link1: do not follow symlinks + FileSystemInfo link1TargetInfo = ResolveLinkTarget(link1Path, returnFinalTarget: false); + Assert.True(link1TargetInfo.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(link1TargetInfo); + Assert.True(link1TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(link2Path, link1TargetInfo.FullName); + Assert.Equal(link2Target, link1TargetInfo.LinkTarget); + + // link2: do not follow symlinks + FileSystemInfo link2TargetInfo = ResolveLinkTarget(link2Path, returnFinalTarget: false); + Assert.True(link2TargetInfo.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(link2TargetInfo); + Assert.False(link2TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(filePath, link2TargetInfo.FullName); + Assert.Null(link2TargetInfo.LinkTarget); + + // link1: follow symlinks + FileSystemInfo finalTarget = ResolveLinkTarget(link1Path, returnFinalTarget: true); + Assert.True(finalTarget.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(finalTarget); + Assert.False(finalTarget.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(filePath, finalTarget.FullName); + } + + protected void CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(bool createOpposite) + { + string tempCwd = GetRandomDirPath(); + Directory.CreateDirectory(tempCwd); + Directory.SetCurrentDirectory(tempCwd); + + // Create a dummy file or directory in cwd. + string fileOrDirectoryInCwd = GetRandomFileName(); + CreateFileOrDirectory(fileOrDirectoryInCwd, createOpposite); + + string oneLevelUpPath = Path.Combine(tempCwd, "one-level-up"); + Directory.CreateDirectory(oneLevelUpPath); + string linkPath = Path.Combine(oneLevelUpPath, GetRandomLinkName()); + + // Create a link with a similar Target Path to the one of our dummy file or directory. + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, fileOrDirectoryInCwd); + FileSystemInfo targetInfo = linkInfo.ResolveLinkTarget(returnFinalTarget: false); + + // Verify that Target is resolved and is relative to Link's directory and not to the cwd. + Assert.False(targetInfo.Exists); + Assert.Equal(Path.GetDirectoryName(linkInfo.FullName), Path.GetDirectoryName(targetInfo.FullName)); + } + + public static IEnumerable ResolveLinkTarget_PathToTarget_Data + { + get + { + foreach (string path in PathToTargetData) + { + yield return new object[] { path, false }; + yield return new object[] { path, true }; + } + } + } + + internal static IEnumerable PathToTargetData + { + get + { + if (OperatingSystem.IsWindows()) + { + //Non-rooted relative + yield return "foo"; + yield return @".\foo"; + yield return @"..\foo"; + // Rooted relative + yield return @"\foo"; + // Rooted absolute + yield return Path.Combine(Path.GetTempPath(), "foo"); + // Extended DOS + yield return Path.Combine(@"\\?\", Path.GetTempPath(), "foo"); + // UNC + yield return @"\\SERVER\share\path"; + } + else + { + //Non-rooted relative + yield return "foo"; + yield return "./foo"; + yield return "../foo"; + // Rooted relative + yield return "/foo"; + // Rooted absolute + Path.Combine(Path.GetTempPath(), "foo"); + } + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs new file mode 100644 index 0000000000000..e82864248eb43 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using Xunit; + +namespace System.IO.Tests +{ + // Contains test methods that can be used for FileInfo and DirectoryInfo. + public abstract class BaseSymbolicLinks_FileSystemInfo : BaseSymbolicLinks_FileSystem + { + // Creates and returns FileSystemInfo instance by calling either the DirectoryInfo or FileInfo constructor and passing the path. + protected abstract FileSystemInfo GetFileSystemInfo(string path); + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + FileSystemInfo link = GetFileSystemInfo(path); + link.CreateAsSymbolicLink(pathToTarget); + return link; + } + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) + => GetFileSystemInfo(linkPath).ResolveLinkTarget(returnFinalTarget); + + private void Delete(string path) + { + if (IsDirectoryTest) + { + Directory.Delete(path); + } + else + { + File.Delete(path); + } + } + + [Fact] + public void LinkTarget_ReturnsNull_NotExists() + { + FileSystemInfo info = GetFileSystemInfo(GetRandomLinkPath()); + Assert.Null(info.LinkTarget); + } + + [Fact] + public void LinkTarget_ReturnsNull_NotALink() + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); + FileSystemInfo info = GetFileSystemInfo(path); + + Assert.True(info.Exists); + Assert.Null(info.LinkTarget); + } + + [Theory] + [MemberData(nameof(LinkTarget_PathToTarget_Data))] + public void LinkTarget_Succeeds(string pathToTarget) + { + FileSystemInfo linkInfo = CreateSymbolicLink(GetRandomLinkPath(), pathToTarget); + + AssertLinkExists(linkInfo); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + } + + [Fact] + public void LinkTarget_RefreshesCorrectly() + { + string path = GetRandomLinkPath(); + string pathToTarget = GetRandomFilePath(); + CreateFileOrDirectory(pathToTarget); + FileSystemInfo linkInfo = CreateSymbolicLink(path, pathToTarget); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + Delete(path); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + linkInfo.Refresh(); + Assert.Null(linkInfo.LinkTarget); + + string newPathToTarget = GetRandomFilePath(); + CreateFileOrDirectory(newPathToTarget); + FileSystemInfo newLinkInfo = CreateSymbolicLink(path, newPathToTarget); + + linkInfo.Refresh(); + Assert.Equal(newPathToTarget, linkInfo.LinkTarget); + Assert.Equal(newLinkInfo.LinkTarget, linkInfo.LinkTarget); + } + + public static IEnumerable LinkTarget_PathToTarget_Data + { + get + { + foreach (string path in PathToTargetData) + { + yield return new object[] { path }; + } + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs new file mode 100644 index 0000000000000..8acc040a8cb69 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + private string GetTestDirectoryActualCasing() => TestDirectory; + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs new file mode 100644 index 0000000000000..4572d48544fcf --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + private const int OPEN_EXISTING = 3; + private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + + // Some Windows versions like Windows Nano Server have the %TEMP% environment variable set to "C:\TEMP" but the + // actual folder name is "C:\Temp", which prevents asserting path values using Assert.Equal due to case sensitiveness. + // So instead of using TestDirectory directly, we retrieve the real path with proper casing of the initial folder path. + private unsafe string GetTestDirectoryActualCasing() + { + try + { + using SafeFileHandle handle = Interop.Kernel32.CreateFile( + TestDirectory, + dwDesiredAccess: 0, + dwShareMode: FileShare.ReadWrite | FileShare.Delete, + dwCreationDisposition: FileMode.Open, + dwFlagsAndAttributes: + OPEN_EXISTING | + FILE_FLAG_BACKUP_SEMANTICS // Necessary to obtain a handle to a directory + ); + + if (!handle.IsInvalid) + { + const int InitialBufferSize = 4096; + char[]? buffer = ArrayPool.Shared.Rent(InitialBufferSize); + uint result = GetFinalPathNameByHandle(handle, buffer); + + // Remove extended prefix + int skip = PathInternal.IsExtended(buffer) ? 4 : 0; + + return new string( + buffer, + skip, + (int)result - skip); + } + } + catch { } + + return TestDirectory; + } + + private unsafe uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) + { + fixed (char* bufPtr = buffer) + { + return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs new file mode 100644 index 0000000000000..d8869ff158121 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + [ConditionalClass(typeof(BaseSymbolicLinks), nameof(CanCreateSymbolicLinks))] + // Contains helper methods that are shared by all symbolic link test classes. + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + protected DirectoryInfo CreateDirectoryContainingSelfReferencingSymbolicLink() + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetRandomDirPath()); + string pathToLink = Path.Join(testDirectory.FullName, GetRandomDirName()); + Assert.True(MountHelper.CreateSymbolicLink(pathToLink, pathToLink, isDirectory: true)); // Create a symlink cycle + return testDirectory; + } + + protected string GetRandomFileName() => GetTestFileName() + ".txt"; + protected string GetRandomLinkName() => GetTestFileName() + ".link"; + protected string GetRandomDirName() => GetTestFileName() + "_dir"; + + protected string GetRandomFilePath() => Path.Join(ActualTestDirectory.Value, GetRandomFileName()); + protected string GetRandomLinkPath() => Path.Join(ActualTestDirectory.Value, GetRandomLinkName()); + protected string GetRandomDirPath() => Path.Join(ActualTestDirectory.Value, GetRandomDirName()); + + private Lazy ActualTestDirectory => new Lazy(() => GetTestDirectoryActualCasing()); + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs index 970c784c7f819..d3d04ca2e5488 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs @@ -1,15 +1,61 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.IO.Tests { - public class Directory_SymbolicLinks : BaseSymbolicLinks + public class Directory_SymbolicLinks : BaseSymbolicLinks_FileSystem { - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + protected override bool IsDirectoryTest => true; + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + Directory.CreateDirectory(path); + } + else + { + File.Create(path).Dispose(); + } + } + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) => + Directory.CreateSymbolicLink(path, pathToTarget); + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) => + Directory.ResolveLinkTarget(linkPath, returnFinalTarget); + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.True(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is DirectoryInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) + { + if (PlatformDetection.IsWindows) + { + Assert.True(link.Exists); + } + else + { + // Unix implementation detail: + // When the directory target does not exist FileStatus.GetExists returns false because: + // - We check _exists (which whould be true because the link itself exists). + // - We check InitiallyDirectory, which is the initial expected object type (which would be true). + // - We check _directory (false because the target directory does not exist) + Assert.False(link.Exists); + } + } + + [Fact] public void EnumerateDirectories_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -19,7 +65,7 @@ public void EnumerateDirectories_LinksWithCycles_ShouldNotThrow() Assert.Equal(expected, Directory.EnumerateDirectories(testDirectory.FullName).Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFiles_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -29,11 +75,22 @@ public void EnumerateFiles_LinksWithCycles_ShouldNotThrow() Assert.Equal(expected, Directory.EnumerateFiles(testDirectory.FullName).Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFileSystemEntries_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); Assert.Single(Directory.EnumerateFileSystemEntries(testDirectory.FullName)); } + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs index 5dedc0b7b4528..e7ee5d0c6727c 100644 --- a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs @@ -1,15 +1,58 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.IO.Tests { - public class DirectoryInfo_SymbolicLinks : BaseSymbolicLinks + public class DirectoryInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo { - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + protected override bool IsDirectoryTest => true; + + protected override FileSystemInfo GetFileSystemInfo(string path) => + new DirectoryInfo(path); + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + Directory.CreateDirectory(path); + } + else + { + File.Create(path).Dispose(); + } + } + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.True(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is DirectoryInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) + { + if (PlatformDetection.IsWindows) + { + Assert.True(link.Exists); + } + else + { + // Unix implementation detail: + // When the directory target does not exist FileStatus.GetExists returns false because: + // - We check _exists (which whould be true because the link itself exists). + // - We check InitiallyDirectory, which is the initial expected object type (which would be true). + // - We check _directory (false because the target directory does not exist) + Assert.False(link.Exists); + } + } + + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateDirectories_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -31,7 +74,7 @@ public void EnumerateDirectories_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLi } } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateFiles_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -53,7 +96,7 @@ public void EnumerateFiles_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bo } } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateFileSystemInfos_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -74,5 +117,16 @@ public void EnumerateFileSystemInfos_LinksWithCycles_ThrowsTooManyLevelsOfSymbol Assert.Throws(() => testDirectory.GetFileSystemInfos("*", options).Count()); } } + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs new file mode 100644 index 0000000000000..4c41fa7bb1657 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.IO.Tests +{ + public class File_SymbolicLinks : BaseSymbolicLinks_FileSystem + { + protected override bool IsDirectoryTest => false; + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + File.Create(path).Dispose(); + } + else + { + Directory.CreateDirectory(path); + } + } + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) => + File.CreateSymbolicLink(path, pathToTarget); + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) => + File.ResolveLinkTarget(linkPath, returnFinalTarget); + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.False(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is FileInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) => + Assert.True(link.Exists); + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs new file mode 100644 index 0000000000000..fa334dd1ca376 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.IO.Tests +{ + public class FileInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo + { + protected override bool IsDirectoryTest => false; + + protected override FileSystemInfo GetFileSystemInfo(string path) => + new FileInfo(path); + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + File.Create(path).Dispose(); + } + else + { + Directory.CreateDirectory(path); + } + } + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.False(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is FileInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) => + Assert.True(link.Exists); + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs index d82346f6f4f10..9300660f1418a 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs @@ -55,32 +55,6 @@ public static TheoryData TrailingSeparators } } - /// - /// In some cases (such as when running without elevated privileges), - /// the symbolic link may fail to create. Only run this test if it creates - /// links successfully. - /// - protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; - - private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => - { - // Verify file symlink creation - string path = Path.GetTempFileName(); - string linkPath = path + ".link"; - bool success = MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false); - try { File.Delete(path); } catch { } - try { File.Delete(linkPath); } catch { } - - // Verify directory symlink creation - path = Path.GetTempFileName(); - linkPath = path + ".link"; - success = success && MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true); - try { Directory.Delete(path); } catch { } - try { Directory.Delete(linkPath); } catch { } - - return success; - }); - public static string GetNamedPipeServerStreamName() { if (PlatformDetection.IsInAppContainer) diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 5c4ca4d01d7c6..dd67a63fed17a 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -22,10 +22,16 @@ + + + + + + diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 91135c4439c97..0decd976d8e53 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -14,13 +14,16 @@ - + + + + @@ -64,12 +67,14 @@ + + @@ -78,13 +83,20 @@ + + + + + + + @@ -137,6 +149,7 @@ + diff --git a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj index d3fd26f707967..d7becfa7963de 100644 --- a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj +++ b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj @@ -46,6 +46,8 @@ + + diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj index 86a7dacccaefd..90acc7605d8ab 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj @@ -19,6 +19,8 @@ Link="SocketCommon\Configuration.cs" /> + + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 05cc719ed051d..ce9ef00df0373 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2659,6 +2659,9 @@ BindHandle for ThreadPool failed on this handle. + + The link's file system entry type is inconsistent with that of its target: {0} + The file '{0}' already exists. @@ -2710,6 +2713,9 @@ The path '{0}' is too long, or a component of the specified path is too long. + + Too many levels of symbolic links in '{0}'. + [Unknown] diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d5c9d5ef37c96..df96b372a881b 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -422,6 +422,7 @@ + @@ -1428,9 +1429,15 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile_IntPtr.cs + + Common\Interop\Windows\Kernel32\Interop.CreateSymbolicLink.cs + Common\Interop\Windows\Kernel32\Interop.CriticalSection.cs + + Common\Interop\Windows\Kernel32\Interop.DeviceIoControl.cs + Common\Interop\Windows\Kernel32\Interop.ExpandEnvironmentStrings.cs @@ -1509,6 +1516,9 @@ Common\Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs + + Common\Interop\Windows\Kernel32\Interop.GetFinalPathNameByHandle.cs + Common\Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs @@ -1620,6 +1630,9 @@ Common\Interop\Windows\Kernel32\Interop.RemoveDirectory.cs + + Common\Interop\Windows\Kernel32\Interop.REPARSE_DATA_BUFFER.cs + Common\Interop\Windows\Kernel32\Interop.ReplaceFile.cs @@ -1893,6 +1906,9 @@ Common\Interop\Unix\Interop.Libraries.cs + + Common\Interop\Unix\Interop.DefaultPathBufferSize.cs + Common\Interop\Unix\System.Native\Interop.Access.cs @@ -2034,6 +2050,9 @@ Common\Interop\Unix\System.Native\Interop.Stat.Span.cs + + Common\Interop\Unix\System.Native\Interop.SymLink.cs + Common\Interop\Unix\System.Native\Interop.SysConf.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index 3fbf585db5de1..f6bc4f42b064d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -315,5 +315,47 @@ public static string[] GetLogicalDrives() { return FileSystem.GetLogicalDrives(); } + + /// + /// Creates a directory symbolic link identified by that points to . + /// + /// The absolute path where the symbolic link should be created. + /// The target directory of the symbolic link. + /// A instance that wraps the newly created directory symbolic link. + /// or is . + /// or is empty. + /// -or- + /// is not an absolute path. + /// -or- + /// or contains invalid path characters. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public static FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + string fullPath = Path.GetFullPath(path); + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + + FileSystem.CreateSymbolicLink(path, pathToTarget, isDirectory: true); + return new DirectoryInfo(originalPath: path, fullPath: fullPath, isNormalized: true); + } + + /// + /// Gets the target of the specified directory link. + /// + /// The path of the directory link. + /// to follow links to the final target; to return the immediate next link. + /// A instance if exists, independently if the target exists or not. if is not a link. + /// The directory on does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); + return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: true); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 626dc761e807b..8480e46a094d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1007,5 +1007,45 @@ private static async Task InternalWriteAllTextAsync(StreamWriter sw, string cont ? Task.FromCanceled(cancellationToken) : InternalWriteAllLinesAsync(AsyncStreamWriter(path, encoding, append: true), contents, cancellationToken); } + + /// + /// Creates a file symbolic link identified by that points to . + /// + /// The path where the symbolic link should be created. + /// The path of the target to which the symbolic link points. + /// A instance that wraps the newly created file symbolic link. + /// or is . + /// or is empty. + /// -or- + /// or contains a null character. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public static FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + string fullPath = Path.GetFullPath(path); + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + + FileSystem.CreateSymbolicLink(path, pathToTarget, isDirectory: false); + return new FileInfo(originalPath: path, fullPath: fullPath, isNormalized: true); + } + + /// + /// Gets the target of the specified file link. + /// + /// The path of the file link. + /// to follow links to the final target; to return the immediate next link. + /// A instance if exists, independently if the target exists or not. if is not a link. + /// The file on does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); + return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: false); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 9a759e7b2b06c..0a4ef7cdef5fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Text; namespace System.IO { @@ -11,6 +12,10 @@ internal static partial class FileSystem { internal const int DefaultBufferSize = 4096; + // On Linux, the maximum number of symbolic links that are followed while resolving a pathname is 40. + // See: https://man7.org/linux/man-pages/man7/path_resolution.7.html + private const int MaxFollowedLinks = 40; + public static void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) { // If the destination path points to a directory, we throw to match Windows behaviour @@ -529,5 +534,91 @@ public static string[] GetLogicalDrives() { return DriveInfoInternal.GetLogicalDrives(); } + + internal static string? GetLinkTarget(ReadOnlySpan linkPath, bool isDirectory) => Interop.Sys.ReadLink(linkPath); + + internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) + { + string pathToTargetFullPath = PathInternal.GetLinkTargetFullPath(path, pathToTarget); + + // Fail if the target exists but is not consistent with the expected filesystem entry type + if (Interop.Sys.Stat(pathToTargetFullPath, out Interop.Sys.FileStatus targetInfo) == 0) + { + if (isDirectory != ((targetInfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR)) + { + throw new IOException(SR.Format(SR.IO_InconsistentLinkType, path)); + } + } + + Interop.CheckIo(Interop.Sys.SymLink(pathToTarget, path), path, isDirectory); + } + + internal static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget, bool isDirectory) + { + ValueStringBuilder sb = new(Interop.DefaultPathBufferSize); + sb.Append(linkPath); + + string? linkTarget = GetLinkTarget(linkPath, isDirectory: false /* Irrelevant in Unix */); + if (linkTarget == null) + { + sb.Dispose(); + Interop.Error error = Interop.Sys.GetLastError(); + // Not a link, return null + if (error == Interop.Error.EINVAL) + { + return null; + } + + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(error), linkPath, isDirectory); + } + + if (!returnFinalTarget) + { + GetLinkTargetFullPath(ref sb, linkTarget); + } + else + { + string? current = linkTarget; + int visitCount = 1; + + while (current != null) + { + if (visitCount > MaxFollowedLinks) + { + sb.Dispose(); + // We went over the limit and couldn't reach the final target + throw new IOException(SR.Format(SR.IO_TooManySymbolicLinkLevels, linkPath)); + } + + GetLinkTargetFullPath(ref sb, current); + current = GetLinkTarget(sb.AsSpan(), isDirectory: false); + visitCount++; + } + } + + Debug.Assert(sb.Length > 0); + linkTarget = sb.ToString(); // ToString disposes + + return isDirectory ? + new DirectoryInfo(linkTarget) : + new FileInfo(linkTarget); + + // In case of link target being relative: + // Preserve the full path of the directory of the previous path + // so the final target is returned with a valid full path + static void GetLinkTargetFullPath(ref ValueStringBuilder sb, ReadOnlySpan linkTarget) + { + if (PathInternal.IsPartiallyQualified(linkTarget)) + { + sb.Length = Path.GetDirectoryNameOffset(sb.AsSpan()); + sb.Append(PathInternal.DirectorySeparatorChar); + } + else + { + sb.Length = 0; + } + sb.Append(linkTarget); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 5b5f9a86bc744..5f88f53a7c539 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.IO; using System.Text; +using System.Buffers; #if MS_IO_REDIST namespace Microsoft.IO @@ -185,7 +186,7 @@ public static void RemoveDirectory(string fullPath, bool recursive) } Interop.Kernel32.WIN32_FIND_DATA findData = default; - GetFindData(fullPath, ref findData); + GetFindData(fullPath, isDirectory: true, ref findData); if (IsNameSurrogateReparsePoint(ref findData)) { // Don't recurse @@ -199,18 +200,16 @@ public static void RemoveDirectory(string fullPath, bool recursive) RemoveDirectoryRecursive(fullPath, ref findData, topLevel: true); } - private static void GetFindData(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData) + private static void GetFindData(string fullPath, bool isDirectory, ref Interop.Kernel32.WIN32_FIND_DATA findData) { - using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.TrimEndingDirectorySeparator(fullPath), ref findData)) + using SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.TrimEndingDirectorySeparator(fullPath), ref findData); + if (handle.IsInvalid) { - if (handle.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - // File not found doesn't make much sense coming from a directory delete. - if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) - errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; - throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - } + int errorCode = Marshal.GetLastWin32Error(); + // File not found doesn't make much sense coming from a directory. + if (isDirectory && errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) + errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } } @@ -407,5 +406,225 @@ public static void SetLastWriteTime(string fullPath, DateTimeOffset time, bool a public static string[] GetLogicalDrives() => DriveInfoInternal.GetLogicalDrives(); + + internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) + { + string pathToTargetFullPath = PathInternal.GetLinkTargetFullPath(path, pathToTarget); + + Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default; + int errorCode = FillAttributeInfo(pathToTargetFullPath, ref data, returnErrorOnNotFound: true); + if (errorCode == Interop.Errors.ERROR_SUCCESS && + data.dwFileAttributes != -1 && + isDirectory != ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0)) + { + throw new IOException(SR.Format(SR.IO_InconsistentLinkType, path)); + } + + Interop.Kernel32.CreateSymbolicLink(path, pathToTarget, isDirectory); + } + + internal static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget, bool isDirectory) + { + string? targetPath = returnFinalTarget ? + GetFinalLinkTarget(linkPath, isDirectory) : + GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: true, returnFullPath: true); + + return targetPath == null ? null : + isDirectory ? new DirectoryInfo(targetPath) : new FileInfo(targetPath); + } + + internal static string? GetLinkTarget(string linkPath, bool isDirectory) + => GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: false); + + /// + /// Gets reparse point information associated to . + /// + /// The immediate link target, absolute or relative or null if the file is not a supported link. + internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnUnreachable, bool returnFullPath) + { + using SafeFileHandle handle = OpenSafeFileHandle(linkPath, + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | + Interop.Kernel32.FileOperations.FILE_FLAG_OPEN_REPARSE_POINT); + + if (handle.IsInvalid) + { + int error = Marshal.GetLastWin32Error(); + + if (!throwOnUnreachable && IsPathUnreachableError(error)) + { + return null; + } + + // File not found doesn't make much sense coming from a directory. + if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND) + { + error = Interop.Errors.ERROR_PATH_NOT_FOUND; + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + byte[] buffer = ArrayPool.Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + try + { + bool success = Interop.Kernel32.DeviceIoControl( + handle, + dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT, + lpInBuffer: IntPtr.Zero, + nInBufferSize: 0, + lpOutBuffer: buffer, + nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + out _, + IntPtr.Zero); + + if (!success) + { + int error = Marshal.GetLastWin32Error(); + // The file or directory is not a reparse point. + if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT) + { + return null; + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + Span bufferSpan = new(buffer); + success = MemoryMarshal.TryRead(bufferSpan, out Interop.Kernel32.REPARSE_DATA_BUFFER rdb); + Debug.Assert(success); + + // Only symbolic links are supported at the moment. + if ((rdb.ReparseTag & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0) + { + return null; + } + + // We use PrintName instead of SubstitutneName given that we don't want to return a NT path when the link wasn't created with such NT path. + // Unlike SubstituteName and GetFinalPathNameByHandle(), PrintName doesn't start with a prefix. + // Another nuance is that SubstituteName does not contain redundant path segments while PrintName does. + // PrintName can ONLY return a NT path if the link was created explicitly targeting a file/folder in such way. e.g: mklink /D linkName \??\C:\path\to\target. + int printNameNameOffset = sizeof(Interop.Kernel32.REPARSE_DATA_BUFFER) + rdb.ReparseBufferSymbolicLink.PrintNameOffset; + int printNameNameLength = rdb.ReparseBufferSymbolicLink.PrintNameLength; + + Span targetPath = MemoryMarshal.Cast(bufferSpan.Slice(printNameNameOffset, printNameNameLength)); + Debug.Assert((rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) == 0 || !PathInternal.IsExtended(targetPath)); + + if (returnFullPath && (rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) != 0) + { + // Target path is relative and is for ResolveLinkTarget(), we need to append the link directory. + return Path.Join(Path.GetDirectoryName(linkPath.AsSpan()), targetPath); + } + + return targetPath.ToString(); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + private static unsafe string? GetFinalLinkTarget(string linkPath, bool isDirectory) + { + Interop.Kernel32.WIN32_FIND_DATA data = default; + GetFindData(linkPath, isDirectory, ref data); + + // The file or directory is not a reparse point. + if ((data.dwFileAttributes & (uint)FileAttributes.ReparsePoint) == 0 || + // Only symbolic links are supported at the moment. + (data.dwReserved0 & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0) + { + return null; + } + + // We try to open the final file since they asked for the final target. + using SafeFileHandle handle = OpenSafeFileHandle(linkPath, + Interop.Kernel32.FileOperations.OPEN_EXISTING | + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS); + + if (handle.IsInvalid) + { + // If the handle fails because it is unreachable, is because the link was broken. + // We need to fallback to manually traverse the links and return the target of the last resolved link. + int error = Marshal.GetLastWin32Error(); + if (IsPathUnreachableError(error)) + { + return GetFinalLinkTargetSlow(linkPath); + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + const int InitialBufferSize = 4096; + char[] buffer = ArrayPool.Shared.Rent(InitialBufferSize); + try + { + uint result = GetFinalPathNameByHandle(handle, buffer); + + // If the function fails because lpszFilePath is too small to hold the string plus the terminating null character, + // the return value is the required buffer size, in TCHARs. This value includes the size of the terminating null character. + if (result > buffer.Length) + { + ArrayPool.Shared.Return(buffer); + buffer = ArrayPool.Shared.Rent((int)result); + + result = GetFinalPathNameByHandle(handle, buffer); + } + + // If the function fails for any other reason, the return value is zero. + if (result == 0) + { + throw Win32Marshal.GetExceptionForLastWin32Error(linkPath); + } + + Debug.Assert(PathInternal.IsExtended(new string(buffer, 0, (int)result).AsSpan())); + // GetFinalPathNameByHandle always returns with extended DOS prefix even if the link target was created without one. + // While this does not interfere with correct behavior, it might be unexpected. + // Hence we trim it if the passed-in path to the link wasn't extended. + int start = PathInternal.IsExtended(linkPath.AsSpan()) ? 0 : 4; + return new string(buffer, start, (int)result - start); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) + { + fixed (char* bufPtr = buffer) + { + return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); + } + } + + string? GetFinalLinkTargetSlow(string linkPath) + { + // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span. + // I am not sure if it's possible to change CreateFile's param to ROS and avoid all these allocations. + string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: true); + string? prev = null; + + while (current != null) + { + prev = current; + current = GetImmediateLinkTarget(current, isDirectory, throwOnUnreachable: false, returnFullPath: true); + } + + return prev; + } + } + + private static unsafe SafeFileHandle OpenSafeFileHandle(string path, int flags) + { + SafeFileHandle handle = Interop.Kernel32.CreateFile( + path, + dwDesiredAccess: 0, + FileShare.ReadWrite | FileShare.Delete, + lpSecurityAttributes: (Interop.Kernel32.SECURITY_ATTRIBUTES*)IntPtr.Zero, + FileMode.Open, + dwFlagsAndAttributes: flags, + hTemplateFile: IntPtr.Zero); + + return handle; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs new file mode 100644 index 0000000000000..a1d506e9218c8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if MS_IO_REDIST +using System; + +namespace Microsoft.IO +#else +namespace System.IO +#endif +{ + internal static partial class FileSystem + { + internal static void VerifyValidPath(string path, string argName) + { + if (path == null) + { + throw new ArgumentNullException(argName); + } + else if (path.Length == 0) + { + throw new ArgumentException(SR.Arg_PathEmpty, argName); + } + else if (path.Contains('\0')) + { + throw new ArgumentException(SR.Argument_InvalidPathChars, argName); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs index 9bcae393120a8..d59c90f23104f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs @@ -27,7 +27,7 @@ internal static FileSystemInfo Create(string fullPath, string fileName, ref File return info; } - internal void Invalidate() => _fileStatus.InvalidateCaches(); + internal void InvalidateCore() => _fileStatus.InvalidateCaches(); internal unsafe void Init(ref FileStatus fileStatus) { @@ -63,7 +63,11 @@ internal DateTimeOffset LastWriteTimeCore internal long LengthCore => _fileStatus.GetLength(FullPath); - public void Refresh() => _fileStatus.RefreshCaches(FullPath); + public void Refresh() + { + _linkTargetIsValid = false; + _fileStatus.RefreshCaches(FullPath); + } internal static void ThrowNotFound(string path) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs index 47d064e8fe173..bb1440851733c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs @@ -42,10 +42,7 @@ internal static unsafe FileSystemInfo Create(string fullPath, ref FileSystemEntr return info; } - internal void Invalidate() - { - _dataInitialized = -1; - } + internal void InvalidateCore() => _dataInitialized = -1; internal unsafe void Init(Interop.NtDll.FILE_FULL_DIR_INFORMATION* info) { @@ -77,7 +74,7 @@ internal bool ExistsCore get { if (_dataInitialized == -1) - Refresh(); + RefreshCore(); if (_dataInitialized != 0) { // Refresh was unable to initialize the data. @@ -145,7 +142,7 @@ private void EnsureDataInitialized() if (_dataInitialized == -1) { _data = default; - Refresh(); + RefreshCore(); } if (_dataInitialized != 0) // Refresh was unable to initialize the data @@ -153,6 +150,12 @@ private void EnsureDataInitialized() } public void Refresh() + { + _linkTargetIsValid = false; + RefreshCore(); + } + + private void RefreshCore() { // This should not throw, instead we store the result so that we can throw it // when someone actually accesses a property diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs index 98bf36b6fd0d6..3beed9e25635e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs @@ -19,11 +19,20 @@ public abstract partial class FileSystemInfo : MarshalByRefObject, ISerializable internal string _name = null!; // Fields initiated in derived classes + private string? _linkTarget; + private bool _linkTargetIsValid; + protected FileSystemInfo(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); } + internal void Invalidate() + { + _linkTargetIsValid = false; + InvalidateCore(); + } + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); @@ -107,6 +116,59 @@ public DateTime LastWriteTimeUtc set => LastWriteTimeCore = File.GetUtcDateTimeOffset(value); } + /// + /// If this instance represents a link, returns the link target's path. + /// If a link does not exist in , or this instance does not represent a link, returns . + /// + public string? LinkTarget + { + get + { + if (_linkTargetIsValid) + { + return _linkTarget; + } + + _linkTarget = FileSystem.GetLinkTarget(FullPath, this is DirectoryInfo); + _linkTargetIsValid = true; + return _linkTarget; + } + } + + /// + /// Creates a symbolic link located in that points to the specified . + /// + /// The path of the symbolic link target. + /// is . + /// is empty. + /// -or- + /// This instance was not created passing an absolute path. + /// -or- + /// contains invalid path characters. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public void CreateAsSymbolicLink(string pathToTarget) + { + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + FileSystem.CreateSymbolicLink(OriginalPath, pathToTarget, this is DirectoryInfo); + Invalidate(); + } + + /// + /// Gets the target of the specified link. + /// + /// to follow links to the final target; to return the immediate next link. + /// A instance if the link exists, independently if the target exists or not; if this file or directory is not a link. + /// The file or directory does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public FileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) => + FileSystem.ResolveLinkTarget(FullPath, returnFinalTarget, this is DirectoryInfo); + /// /// Returns the original path. Use FullName or Name properties for the full path or file/directory name. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index 104704de7b715..cdc96b5f45228 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -125,7 +125,7 @@ public static ReadOnlySpan GetDirectoryName(ReadOnlySpan path) return end >= 0 ? path.Slice(0, end) : ReadOnlySpan.Empty; } - private static int GetDirectoryNameOffset(ReadOnlySpan path) + internal static int GetDirectoryNameOffset(ReadOnlySpan path) { int rootLength = PathInternal.GetRootLength(path); int end = path.Length; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs index fcbeece65a73f..ff9d79caee253 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs @@ -245,5 +245,13 @@ internal static ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan internal static bool EndsInDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]); + + internal static string GetLinkTargetFullPath(string path, string pathToTarget) + => IsPartiallyQualified(pathToTarget.AsSpan()) ? +#if MS_IO_REDIST + Path.Combine(Path.GetDirectoryName(path), pathToTarget) : pathToTarget; +#else + Path.Join(Path.GetDirectoryName(path.AsSpan()), pathToTarget.AsSpan()) : pathToTarget; +#endif } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 67adfd0c6d35c..1b9b1d241b854 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10175,6 +10175,7 @@ public override void WriteByte(byte value) { } public static partial class Directory { public static System.IO.DirectoryInfo CreateDirectory(string path) { throw null; } + public static System.IO.FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) { throw null; } public static void Delete(string path) { } public static void Delete(string path, bool recursive) { } public static System.Collections.Generic.IEnumerable EnumerateDirectories(string path) { throw null; } @@ -10213,6 +10214,7 @@ public static void Delete(string path, bool recursive) { } public static string[] GetLogicalDrives() { throw null; } public static System.IO.DirectoryInfo? GetParent(string path) { throw null; } public static void Move(string sourceDirName, string destDirName) { } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } public static void SetCurrentDirectory(string path) { } @@ -10237,10 +10239,13 @@ protected FileSystemInfo(System.Runtime.Serialization.SerializationInfo info, Sy public System.DateTime LastAccessTimeUtc { get { throw null; } set { } } public System.DateTime LastWriteTime { get { throw null; } set { } } public System.DateTime LastWriteTimeUtc { get { throw null; } set { } } + public string? LinkTarget { get { throw null; } } public abstract string Name { get; } + public void CreateAsSymbolicLink(string pathToTarget) { } public abstract void Delete(); public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public void Refresh() { } + public System.IO.FileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) { throw null; } public override string ToString() { throw null; } } public sealed partial class DirectoryInfo : System.IO.FileSystemInfo @@ -10325,6 +10330,7 @@ public static void Copy(string sourceFileName, string destFileName, bool overwri public static System.IO.FileStream Create(string path) { throw null; } public static System.IO.FileStream Create(string path, int bufferSize) { throw null; } public static System.IO.FileStream Create(string path, int bufferSize, System.IO.FileOptions options) { throw null; } + public static System.IO.FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) { throw null; } public static System.IO.StreamWriter CreateText(string path) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static void Decrypt(string path) { } @@ -10363,6 +10369,7 @@ public static void Move(string sourceFileName, string destFileName, bool overwri public static System.Collections.Generic.IEnumerable ReadLines(string path, System.Text.Encoding encoding) { throw null; } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName) { } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) { } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } public static void SetAttributes(string path, System.IO.FileAttributes fileAttributes) { } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 2c0921f9fcd93..505d487f1d486 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -324,10 +324,14 @@ Link="Common\Interop\Unix\Interop.Libraries.cs" /> + + Date: Fri, 9 Jul 2021 13:48:27 +0200 Subject: [PATCH 19/72] use fstatfs to detect whether current file system supports shared locks for files opened for writing (#55256) --- .../Interop.MountPoints.FormatInfo.cs | 123 --------------- .../Interop.UnixFileSystemTypes.cs | 145 ++++++++++++++++++ .../Native/Unix/Common/pal_config.h.in | 2 + .../Native/Unix/System.Native/entrypoints.c | 1 + .../Native/Unix/System.Native/pal_io.c | 19 +++ .../Native/Unix/System.Native/pal_io.h | 5 + src/libraries/Native/Unix/configure.cmake | 22 +++ .../src/System.IO.FileSystem.DriveInfo.csproj | 2 + .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 49 +++++- .../System.Private.CoreLib.Shared.projitems | 3 + 10 files changed, 244 insertions(+), 127 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs index fb59b17534680..05d74c84e789d 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs @@ -22,129 +22,6 @@ static Sys() private const int MountPointFormatBufferSizeInBytes = 32; - /// - /// Internal FileSystem names and magic numbers taken from man(2) statfs - /// - /// - /// These value names MUST be kept in sync with those in GetDriveType below, - /// where this enum must be a subset of the GetDriveType list, with the enum - /// values here exactly matching a string there. - /// - internal enum UnixFileSystemTypes : long - { - adfs = 0xADF5, - affs = 0xADFF, - afs = 0x5346414F, - anoninode = 0x09041934, - aufs = 0x61756673, - autofs = 0x0187, - autofs4 = 0x6D4A556D, - befs = 0x42465331, - bdevfs = 0x62646576, - bfs = 0x1BADFACE, - binfmt_misc = 0x42494E4D, - bootfs = 0xA56D3FF9, - btrfs = 0x9123683E, - ceph = 0x00C36400, - cgroupfs = 0x0027E0EB, - cgroup2fs = 0x63677270, - cifs = 0xFF534D42, - coda = 0x73757245, - coherent = 0x012FF7B7, - configfs = 0x62656570, - cramfs = 0x28CD3D45, - debugfs = 0x64626720, - devfs = 0x1373, - devpts = 0x1CD1, - ecryptfs = 0xF15F, - efs = 0x00414A53, - exofs = 0x5DF5, - ext = 0x137D, - ext2_old = 0xEF51, - ext2 = 0xEF53, - ext3 = 0xEF53, - ext4 = 0xEF53, - fat = 0x4006, - fd = 0xF00D1E, - fhgfs = 0x19830326, - fuse = 0x65735546, - fuseblk = 0x65735546, - fusectl = 0x65735543, - futexfs = 0x0BAD1DEA, - gfsgfs2 = 0x1161970, - gfs2 = 0x01161970, - gpfs = 0x47504653, - hfs = 0x4244, - hfsplus = 0x482B, - hpfs = 0xF995E849, - hugetlbfs = 0x958458F6, - inodefs = 0x11307854, - inotifyfs = 0x2BAD1DEA, - isofs = 0x9660, - // isofs = 0x4004, // R_WIN - // isofs = 0x4000, // WIN - jffs = 0x07C0, - jffs2 = 0x72B6, - jfs = 0x3153464A, - kafs = 0x6B414653, - lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */ - logfs = 0xC97E8168, - lustre = 0x0BD00BD0, - minix_old = 0x137F, /* orig. minix */ - minix = 0x138F, /* 30 char minix */ - minix2 = 0x2468, /* minix V2 */ - minix2v2 = 0x2478, /* MINIX V2, 30 char names */ - minix3 = 0x4D5A, - mqueue = 0x19800202, - msdos = 0x4D44, - nfs = 0x6969, - nfsd = 0x6E667364, - nilfs = 0x3434, - novell = 0x564C, - ntfs = 0x5346544E, - openprom = 0x9FA1, - ocfs2 = 0x7461636F, - omfs = 0xC2993D87, - overlay = 0x794C7630, - overlayfs = 0x794C764F, - panfs = 0xAAD7AAEA, - pipefs = 0x50495045, - proc = 0x9FA0, - pstorefs = 0x6165676C, - qnx4 = 0x002F, - qnx6 = 0x68191122, - ramfs = 0x858458F6, - reiserfs = 0x52654973, - romfs = 0x7275, - rootfs = 0x53464846, - rpc_pipefs = 0x67596969, - samba = 0x517B, - securityfs = 0x73636673, - selinux = 0xF97CFF8C, - smb = 0x517B, - sockfs = 0x534F434B, - squashfs = 0x73717368, - sysfs = 0x62656572, - sysv2 = 0x012FF7B6, - sysv4 = 0x012FF7B5, - tmpfs = 0x01021994, - ubifs = 0x24051905, - udf = 0x15013346, - ufs = 0x00011954, - ufscigam = 0x54190100, // ufs byteswapped - ufs2 = 0x19540119, - usbdevice = 0x9FA2, - v9fs = 0x01021997, - vmhgfs = 0xBACBACBC, - vxfs = 0xA501FCF5, - vzfs = 0x565A4653, - xenfs = 0xABBA1974, - xenix = 0x012FF7B4, - xfs = 0x58465342, - xia = 0x012FD16D, - zfs = 0x2FC12FC1, - } - [StructLayout(LayoutKind.Sequential)] internal struct MountPointInformation { diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs new file mode 100644 index 0000000000000..fd34f87418223 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Internal FileSystem names and magic numbers taken from man(2) statfs + /// + /// + /// These value names MUST be kept in sync with those in GetDriveType (moved to Interop.MountPoints.FormatInfo.cs), + /// where this enum must be a subset of the GetDriveType list, with the enum + /// values here exactly matching a string there. + /// + internal enum UnixFileSystemTypes : long + { + adfs = 0xADF5, + affs = 0xADFF, + afs = 0x5346414F, + anoninode = 0x09041934, + aufs = 0x61756673, + autofs = 0x0187, + autofs4 = 0x6D4A556D, + befs = 0x42465331, + bdevfs = 0x62646576, + bfs = 0x1BADFACE, + binfmt_misc = 0x42494E4D, + bootfs = 0xA56D3FF9, + btrfs = 0x9123683E, + ceph = 0x00C36400, + cgroupfs = 0x0027E0EB, + cgroup2fs = 0x63677270, + cifs = 0xFF534D42, + coda = 0x73757245, + coherent = 0x012FF7B7, + configfs = 0x62656570, + cramfs = 0x28CD3D45, + debugfs = 0x64626720, + devfs = 0x1373, + devpts = 0x1CD1, + ecryptfs = 0xF15F, + efs = 0x00414A53, + exofs = 0x5DF5, + ext = 0x137D, + ext2_old = 0xEF51, + ext2 = 0xEF53, + ext3 = 0xEF53, + ext4 = 0xEF53, + fat = 0x4006, + fd = 0xF00D1E, + fhgfs = 0x19830326, + fuse = 0x65735546, + fuseblk = 0x65735546, + fusectl = 0x65735543, + futexfs = 0x0BAD1DEA, + gfsgfs2 = 0x1161970, + gfs2 = 0x01161970, + gpfs = 0x47504653, + hfs = 0x4244, + hfsplus = 0x482B, + hpfs = 0xF995E849, + hugetlbfs = 0x958458F6, + inodefs = 0x11307854, + inotifyfs = 0x2BAD1DEA, + isofs = 0x9660, + // isofs = 0x4004, // R_WIN + // isofs = 0x4000, // WIN + jffs = 0x07C0, + jffs2 = 0x72B6, + jfs = 0x3153464A, + kafs = 0x6B414653, + lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */ + logfs = 0xC97E8168, + lustre = 0x0BD00BD0, + minix_old = 0x137F, /* orig. minix */ + minix = 0x138F, /* 30 char minix */ + minix2 = 0x2468, /* minix V2 */ + minix2v2 = 0x2478, /* MINIX V2, 30 char names */ + minix3 = 0x4D5A, + mqueue = 0x19800202, + msdos = 0x4D44, + nfs = 0x6969, + nfsd = 0x6E667364, + nilfs = 0x3434, + novell = 0x564C, + ntfs = 0x5346544E, + openprom = 0x9FA1, + ocfs2 = 0x7461636F, + omfs = 0xC2993D87, + overlay = 0x794C7630, + overlayfs = 0x794C764F, + panfs = 0xAAD7AAEA, + pipefs = 0x50495045, + proc = 0x9FA0, + pstorefs = 0x6165676C, + qnx4 = 0x002F, + qnx6 = 0x68191122, + ramfs = 0x858458F6, + reiserfs = 0x52654973, + romfs = 0x7275, + rootfs = 0x53464846, + rpc_pipefs = 0x67596969, + samba = 0x517B, + securityfs = 0x73636673, + selinux = 0xF97CFF8C, + smb = 0x517B, + smb2 = 0xFE534D42, + sockfs = 0x534F434B, + squashfs = 0x73717368, + sysfs = 0x62656572, + sysv2 = 0x012FF7B6, + sysv4 = 0x012FF7B5, + tmpfs = 0x01021994, + ubifs = 0x24051905, + udf = 0x15013346, + ufs = 0x00011954, + ufscigam = 0x54190100, // ufs byteswapped + ufs2 = 0x19540119, + usbdevice = 0x9FA2, + v9fs = 0x01021997, + vmhgfs = 0xBACBACBC, + vxfs = 0xA501FCF5, + vzfs = 0x565A4653, + xenfs = 0xABBA1974, + xenix = 0x012FF7B4, + xfs = 0x58465342, + xia = 0x012FD16D, + zfs = 0x2FC12FC1, + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetFileSystemType")] + private static extern long GetFileSystemType(SafeFileHandle fd); + + internal static bool TryGetFileSystemType(SafeFileHandle fd, out UnixFileSystemTypes fileSystemType) + { + long fstatfsResult = GetFileSystemType(fd); + fileSystemType = (UnixFileSystemTypes)fstatfsResult; + return fstatfsResult != -1; + } + } +} diff --git a/src/libraries/Native/Unix/Common/pal_config.h.in b/src/libraries/Native/Unix/Common/pal_config.h.in index 2b86ed15d4cae..9a02610534101 100644 --- a/src/libraries/Native/Unix/Common/pal_config.h.in +++ b/src/libraries/Native/Unix/Common/pal_config.h.in @@ -5,6 +5,8 @@ #cmakedefine01 HAVE_MMAP64 #cmakedefine01 HAVE_FTRUNCATE64 #cmakedefine01 HAVE_POSIX_FADVISE64 +#cmakedefine01 HAVE_STATFS_VFS +#cmakedefine01 HAVE_STATFS_MOUNT #cmakedefine01 HAVE_FLOCK64 #cmakedefine01 HAVE_F_DUPFD_CLOEXEC #cmakedefine01 HAVE_F_FULLFSYNC diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index f39360810b57e..bf6f98953f9a9 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -104,6 +104,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_INotifyRemoveWatch) DllImportEntry(SystemNative_RealPath) DllImportEntry(SystemNative_GetPeerID) + DllImportEntry(SystemNative_GetFileSystemType) DllImportEntry(SystemNative_LockFileRegion) DllImportEntry(SystemNative_LChflags) DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 9e02aa7c2e65d..59b8f2770c103 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -35,6 +35,11 @@ #if HAVE_INOTIFY #include #endif +#if HAVE_STATFS_VFS // Linux +#include +#elif HAVE_STATFS_MOUNT // BSD +#include +#endif #ifdef _AIX #include @@ -1386,6 +1391,20 @@ static int16_t ConvertLockType(int16_t managedLockType) } } +int64_t SystemNative_GetFileSystemType(intptr_t fd) +{ +#if HAVE_STATFS_VFS || HAVE_STATFS_MOUNT + int statfsRes; + struct statfs statfsArgs; + // for our needs (get file system type) statfs is always enough and there is no need to use statfs64 + // which got deprecated in macOS 10.6, in favor of statfs + while ((statfsRes = fstatfs(ToFileDescriptor(fd), &statfsArgs)) == -1 && errno == EINTR) ; + return statfsRes == -1 ? (int64_t)-1 : (int64_t)statfsArgs.f_type; +#else + #error "Platform doesn't support fstatfs" +#endif +} + int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType) { int16_t unixLockType = ConvertLockType(lockType); diff --git a/src/libraries/Native/Unix/System.Native/pal_io.h b/src/libraries/Native/Unix/System.Native/pal_io.h index 6461881a91eb1..3f0db054d4368 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.h +++ b/src/libraries/Native/Unix/System.Native/pal_io.h @@ -717,6 +717,11 @@ PALEXPORT char* SystemNative_RealPath(const char* path); */ PALEXPORT int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid); +/** +* Returns file system type on success, or -1 on error. +*/ +PALEXPORT int64_t SystemNative_GetFileSystemType(intptr_t fd); + /** * Attempts to lock/unlock the region of the file "fd" specified by the offset and length. lockType * can be set to F_UNLCK (2) for unlock or F_WRLCK (3) for lock. diff --git a/src/libraries/Native/Unix/configure.cmake b/src/libraries/Native/Unix/configure.cmake index b2cfdb3bc40c2..18afacc2dbc59 100644 --- a/src/libraries/Native/Unix/configure.cmake +++ b/src/libraries/Native/Unix/configure.cmake @@ -108,6 +108,28 @@ check_c_source_compiles( # /in_pktinfo +check_c_source_compiles( + " + #include + int main(void) + { + struct statfs s; + return 0; + } + " + HAVE_STATFS_VFS) + +check_c_source_compiles( + " + #include + int main(void) + { + struct statfs s; + return 0; + } + " + HAVE_STATFS_MOUNT) + check_c_source_compiles( " #include diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj index c516eb543e334..d07e89ec979be 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj @@ -56,6 +56,8 @@ Link="Common\Interop\Unix\Interop.Libraries.cs" /> + = 0)) { // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or @@ -320,6 +327,40 @@ private void Init(string path, FileMode mode, FileAccess access, FileShare share } } + private bool CanLockTheFile(Interop.Sys.LockOperations lockOperation, FileAccess access) + { + Debug.Assert(lockOperation == Interop.Sys.LockOperations.LOCK_EX || lockOperation == Interop.Sys.LockOperations.LOCK_SH); + + if (DisableFileLocking) + { + return false; + } + else if (lockOperation == Interop.Sys.LockOperations.LOCK_EX) + { + return true; // LOCK_EX is always OK + } + else if ((access & FileAccess.Write) == 0) + { + return true; // LOCK_SH is always OK when reading + } + + if (!Interop.Sys.TryGetFileSystemType(this, out Interop.Sys.UnixFileSystemTypes unixFileSystemType)) + { + return false; // assume we should not acquire the lock if we don't know the File System + } + + switch (unixFileSystemType) + { + case Interop.Sys.UnixFileSystemTypes.nfs: // #44546 + case Interop.Sys.UnixFileSystemTypes.smb: + case Interop.Sys.UnixFileSystemTypes.smb2: // #53182 + case Interop.Sys.UnixFileSystemTypes.cifs: + return false; // LOCK_SH is not OK when writing to NFS, CIFS or SMB + default: + return true; // in all other situations it should be OK + } + } + private bool GetCanSeek() { Debug.Assert(!IsClosed); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index df96b372a881b..a1e270dbde97d 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1927,6 +1927,9 @@ Common\Interop\Unix\System.Native\Interop.ErrNo.cs + + Common\Interop\Unix\System.Native\Interop.UnixFileSystemTypes.cs + Common\Interop\Unix\System.Native\Interop.FLock.cs From cb1beaac6fc314b0a91a79179184fe88f2fa601b Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 9 Jul 2021 15:19:54 +0300 Subject: [PATCH 20/72] Remove redundant pinvokes in S.N.Http/Mail on tvOS (#54812) Addressed xamarin/xamarin-macios#12011 (comment) --- .../src/System/Net/NTAuthentication.Common.cs | 2 ++ .../src/System.Net.Http.csproj | 33 ++++++++++++------- .../AuthenticationHelper.NtAuth.tvOS.cs | 30 +++++++++++++++++ .../src/System.Net.Mail.csproj | 18 ++++++---- 4 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs diff --git a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs index b98f271183145..148d1dc9a0700 100644 --- a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs @@ -5,10 +5,12 @@ using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Security.Authentication.ExtendedProtection; namespace System.Net { + [UnsupportedOSPlatform("tvos")] internal sealed partial class NTAuthentication { private bool _isServer; diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 613416b90aa78..bea0e12f9fe00 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -143,7 +143,7 @@ - + @@ -192,7 +192,7 @@ - @@ -324,28 +324,37 @@ Link="Common\System\Net\Security\Unix\SafeFreeCredentials.cs" /> - - - - - + + + + + + + + + + + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs new file mode 100644 index 0000000000000..86b2eacbb1845 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + internal static partial class AuthenticationHelper + { + private static Task InnerSendAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + return isProxyAuth ? + SendWithProxyAuthAsync(request, authUri, async, credentials, true, connectionPool, cancellationToken).AsTask() : + SendWithRequestAuthAsync(request, async, credentials, true, connectionPool, cancellationToken).AsTask(); + } + + public static Task SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, bool async, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + return InnerSendAsync(request, proxyUri, async, proxyCredentials, isProxyAuth: true, connection, connectionPool, cancellationToken); + } + + public static Task SendWithNtConnectionAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + Debug.Assert(request.RequestUri != null); + return InnerSendAsync(request, request.RequestUri, async, credentials, isProxyAuth: false, connection, connectionPool, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj index b3350746d8182..a90d602beadd4 100644 --- a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj +++ b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj @@ -153,17 +153,23 @@ Link="Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs" /> - - - + + + + + + + + Date: Fri, 9 Jul 2021 16:01:33 +0300 Subject: [PATCH 21/72] Make LambdaCompiler prefer interpretation to compilation on iOS (#54970) This is an attempt to address #47112 based on Egor's suggestion. The changes: - set IsInterpreting MSBuild property to 'true' in case of iOS; - made System.Linq.Expressions.ExpressionCreator class internal static in order not to introduce new public type; otherwise it throws Type 'System.Linq.Expressions.ExpressionCreator' does not exist in the reference but it does exist in the implementation. -updated the iOS simulator functional test for AOT to verify the fix - added PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly which checks whether the System.Linq.Expressions is built with IsInterpreting property set to true. To do so, it uses reflection to verify the Expression.Accept method doesn't exist. - disabled some failing library tests using ActiveIssue + PlatformDetection. - updated the invariant functional test for the iOS simulator (b/c of Allow restricting cultures creation with any arbitrary names in Globalization Invariant Mode #54247) Co-authored-by: Stephen Toub --- .../TestUtilities/System/PlatformDetection.cs | 17 +++++++++ .../tests/NewLateBindingTests.cs | 2 +- ...rmance.dynamic.context.indexer.genclass.cs | 1 + ...dynamic.context.operator.compound.basic.cs | 2 ++ ...ic.declarations.formalParameter.Methods.cs | 36 +++++++++++++++++++ ...eclarations.localVariable.blockVariable.cs | 3 ++ ...ormance.dynamic.dynamicType.conversions.cs | 3 ++ ...loadResolution.Indexers.1class2indexers.cs | 3 ++ ...erloadResolution.Methods.1class2methods.cs | 3 ++ .../Conformance.dynamic.Variance.assign.cs | 3 ++ .../tests/System.Dynamic.Runtime.Tests.csproj | 2 +- .../src/System.Linq.Expressions.csproj | 3 +- .../Linq/Expressions/LambdaExpression.cs | 2 +- .../tests/Array/ArrayArrayIndexTests.cs | 1 - .../tests/Array/ArrayIndexTests.cs | 1 - .../tests/Array/NullableArrayIndexTests.cs | 1 - .../Arithmetic/BinaryModuloTests.cs | 1 - .../tests/Block/NoParameterBlockTests.cs | 1 - .../tests/Cast/CastTests.cs | 1 - .../tests/Dynamic/InvokeMemberBindingTests.cs | 2 ++ .../tests/Member/MemberAccessTests.cs | 1 - .../tests/SequenceTests/SequenceTests.cs | 1 - .../System.Linq.Expressions.Tests.csproj | 3 +- .../tests/TypeBinary/TypeIs.cs | 1 - .../tests/Unary/UnaryConvertTests.cs | 1 - .../tests/Unary/UnaryUnboxTests.cs | 1 - src/libraries/tests.proj | 3 ++ .../iOS/Simulator/AOT/Program.cs | 3 +- .../InvariantCultureOnlyMode/Program.cs | 12 +++++-- .../Simulator/LambdaCompilerAOT/Program.cs | 28 +++++++++++++++ ...OS.Simulator.LambdaCompilerAot.Test.csproj | 19 ++++++++++ 31 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index fc829692c03f4..5038c4f01977e 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; +using System.Linq.Expressions; using System.Security; using System.Security.Authentication; using System.Reflection; @@ -66,6 +67,22 @@ public static partial class PlatformDetection public static bool IsUsingLimitedCultures => !IsNotMobile; public static bool IsNotUsingLimitedCultures => IsNotMobile; + public static bool IsLinqExpressionsBuiltWithIsInterpretingOnly => s_LinqExpressionsBuiltWithIsInterpretingOnly.Value; + public static bool IsNotLinqExpressionsBuiltWithIsInterpretingOnly => !IsLinqExpressionsBuiltWithIsInterpretingOnly; + private static readonly Lazy s_LinqExpressionsBuiltWithIsInterpretingOnly = new Lazy(GetLinqExpressionsBuiltWithIsInterpretingOnly); + private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() + { + Type type = typeof(LambdaExpression); + if (type != null) + { + // The "Accept" method is under FEATURE_COMPILE conditional so it should not exist + MethodInfo methodInfo = type.GetMethod("Accept", BindingFlags.NonPublic | BindingFlags.Static); + return methodInfo == null; + } + + return false; + } + // Please make sure that you have the libgdiplus dependency installed. // For details, see https://docs.microsoft.com/dotnet/core/install/dependencies?pivots=os-macos&tabs=netcore31#libgdiplus public static bool IsDrawingSupported diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs index 5437590241a62..38b21a9eb11cc 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs @@ -94,7 +94,7 @@ public static IEnumerable LateCall_OptionalValues_Data() static object[] CreateData(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) => new object[] { memberName, arguments, typeArguments, expectedValue }; } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51834", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] [MemberData(nameof(LateCall_OptionalValues_Data))] public void LateCall_OptionalValues(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs index a31ab30422ee3..79d677b3c2abf 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs @@ -774,6 +774,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.indexer.genclas public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55118", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void TryCatchFinally() { dynamic dy = new MemberClass(); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs index 0c49adf396b6c..381b89b819864 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs @@ -239,10 +239,12 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.operate.compoun // // // + using System; public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs index f6ceeddfa49ee..6306863d44ea3 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs @@ -98,6 +98,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.extensionmethod006.extensionmethod006 { + using System; + static // Extension method that extends dynamic // // @@ -115,6 +117,7 @@ public static string Foo(this object x, dynamic d) public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -189,6 +192,8 @@ public static int Method(this Test t, int value) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method001.method001 { + using System; + public class Test { private static bool s_ok = false; @@ -203,6 +208,7 @@ public void Foo(dynamic d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -260,6 +266,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method003.method003 { + using System; + public class Test { private static bool s_ok = false; @@ -273,6 +281,7 @@ public void Foo(dynamic d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -294,6 +303,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method004.method004 { + using System; + public class Test { private static bool s_ok = false; @@ -306,6 +317,7 @@ public void Foo(ref dynamic d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -330,6 +342,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method005.method005 { + using System; + public class Test { private static bool s_ok = false; @@ -345,6 +359,7 @@ public void Foo(params dynamic[] d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -367,6 +382,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method007.method007 { + using System; + public class Test { private static bool s_ok = false; @@ -380,6 +397,7 @@ public void Foo(dynamic d, dynamic d2) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -403,6 +421,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method008.method008 { + using System; + public class Test { private static bool s_ok = false; @@ -416,6 +436,7 @@ public void Foo(dynamic d, int d2) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -438,6 +459,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method009.method009 { + using System; + public class Test { private static bool s_ok = false; @@ -451,6 +474,7 @@ public void Foo(dynamic d, dynamic d2, dynamic d3) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -473,6 +497,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method010.method010 { + using System; + public class Test { private static bool s_ok = false; @@ -488,6 +514,7 @@ public MyClass(params dynamic[] d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -509,6 +536,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method011.method011 { + using System; + public class Test { private static bool s_ok = false; @@ -523,6 +552,7 @@ public static void Foo(dynamic d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -543,6 +573,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method012.method012 { + using System; + public class Test { private static bool s_ok = false; @@ -555,6 +587,7 @@ public void Foo(ref dynamic d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -579,6 +612,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method013.method013 { + using System; + public class Test { private static bool s_ok = false; @@ -591,6 +626,7 @@ public void Foo(T d) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs index 1aa3f5b55af1e..46c8372b14a31 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs @@ -1189,9 +1189,12 @@ from d in numbers namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.localVariable.blockVariable.trycatch002.trycatch002 { + using System; + public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs index d27e05bff2864..cb63fac799539 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs @@ -4356,6 +4356,8 @@ public static int MainMethod() namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions.dlgate003.dlgate003 { + using System; + // Delegate conversions // // Tests to figure out if the right conversion from method groups to delegates are applied @@ -4375,6 +4377,7 @@ public static object Foo() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs index 4a3ef8ebc82eb..5b08d4e447ed6 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs @@ -665,6 +665,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Indexers.Oneclass2indexers.onedynamicparam004.onedynamicparam004 { + using System; + // Tests overload resolution for 1 class and 2 methods // // @@ -720,6 +722,7 @@ public object this[object x] public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs index 5cf0a1ffe9dfd..72f5998f65690 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs @@ -501,6 +501,8 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Methods.Oneclass2methods.onedynamicparam004.onedynamicparam004 { + using System; + // Tests overload resolution for 1 class and 2 methods // // @@ -530,6 +532,7 @@ public void Method(object x) public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs index 7e277d276d4c3..d9dd1adb35618 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs @@ -476,6 +476,8 @@ public static int MainMethod() namespace ManagedTests.DynamicCSharp.Conformance.dynamic.Variance.assign.assignment07.assignment07 { + using System; + // variance // assignment Contravariant delegates // contravariance on delegates assigned to arrays @@ -499,6 +501,7 @@ public class C private static dynamic s_array3; [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55119", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj b/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj index 9b70ef3b077cf..57195bfe5aad1 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj +++ b/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj @@ -2,7 +2,7 @@ true 67,168,219,414,162,184,458,464,78,169,114,693,108,1981,649,109,1066,3021,3026,3002,3014,3022,660,661,429;xUnit1013 - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst diff --git a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj index 7972cdb54f1fa..0a9d7eba08b66 100644 --- a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj +++ b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj @@ -1,9 +1,10 @@ true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst enable false + true $(DefineConstants);FEATURE_DLG_INVOKE;FEATURE_FAST_CREATE $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs index a615e89056abd..0c591d6f3ae05 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs @@ -327,7 +327,7 @@ internal static Expression Create(Expression body, string? name, bool #if !FEATURE_COMPILE // Separate expression creation class to hide the CreateExpressionFunc function from users reflecting on Expression - public class ExpressionCreator + internal static class ExpressionCreator { public static Expression CreateExpressionFunc(Expression body, string? name, bool tailCall, ReadOnlyCollection parameters) { diff --git a/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs index 9430253687acb..1bd056bba9af3 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs @@ -5,7 +5,6 @@ namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class ArrayArrayIndexTests { #region Boolean tests diff --git a/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs index 6eed3897f93d2..4707926123263 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs @@ -5,7 +5,6 @@ namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class ArrayIndexTests { #region Boolean tests diff --git a/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs index e0932eef784ee..91754a96bc27e 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs @@ -5,7 +5,6 @@ namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class NullableArrayIndexTests { #region NullableBool tests diff --git a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs index 4873b7769c377..c1a41f30861c2 100644 --- a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs @@ -107,7 +107,6 @@ public static void CheckULongModuloTest(bool useInterpreter) [Theory] [ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void CheckLongModuloTest(bool useInterpreter) { long[] array = new long[] { 0, 1, -1, long.MinValue, long.MaxValue }; diff --git a/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs b/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs index 9848e3a86f918..4891d348c29bb 100644 --- a/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs @@ -292,7 +292,6 @@ public void ResultPropertyFromParams(object value, int blockSize) [Theory] [MemberData(nameof(ConstantValuesAndSizes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public void ResultPropertyFromEnumerable(object value, int blockSize) { ConstantExpression constant = Expression.Constant(value, value.GetType()); diff --git a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs index bb83b061ec64c..090878f8fee7e 100644 --- a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs @@ -7,7 +7,6 @@ namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class CastTests { #region Test methods diff --git a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs index f5abaf81ae129..ee4962af5ff1c 100644 --- a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs @@ -54,6 +54,7 @@ public static IEnumerable ObjectArguments() yield return new[] {new object()}; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55070", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] [Theory, MemberData(nameof(ObjectArguments))] public void InvokeVirtualMethod(object value) { @@ -411,6 +412,7 @@ public Func Delegate public OutAction OutDelegate; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55071", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] [Fact] public void InvokeFuncMember() { diff --git a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs index 497416457ed07..cca9a0e08c85b 100644 --- a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs @@ -8,7 +8,6 @@ namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class MemberAccessTests { private class UnreadableIndexableClass diff --git a/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs b/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs index 44f50b5bdf894..871721f81f4d2 100644 --- a/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs @@ -1486,7 +1486,6 @@ private static S TestConvertChecked(T value, bool useInterpreter) [Theory] [ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void ConvertNullToInt(bool useInterpreter) { Assert.Throws(() => diff --git a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj index b0a74eabbce8f..c2efe998ee96a 100644 --- a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj +++ b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj @@ -2,9 +2,10 @@ true false + true $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst diff --git a/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs b/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs index f789d71f3b0f4..26edcd31eff7e 100644 --- a/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs +++ b/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs @@ -57,7 +57,6 @@ public void CannotReduce() [Theory] [MemberData(nameof(ExpressionAndTypeCombinations))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public void TypePropertyMatches(Expression expression, Type type) { Assert.Equal(type, Expression.TypeIs(expression, type).TypeOperand); diff --git a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs index 4cf5c45f2df9a..d0b82224e6734 100644 --- a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs @@ -57,7 +57,6 @@ public static void CheckUnaryConvertBooleanToNumericTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void ConvertNullToNonNullableValueTest(bool useInterpreter) { foreach (var e in ConvertNullToNonNullableValue()) diff --git a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs index 184828d0ff46b..2ec61247eab7f 100644 --- a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs @@ -10,7 +10,6 @@ public static class UnaryUnboxTests #region Test methods [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void CheckUnaryUnboxTest(bool useInterpreter) { VerifyUnbox(42, typeof(int), false, useInterpreter); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 1460b3be14140..b1f20442aac7e 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -205,6 +205,9 @@ + + + diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs index 437f6843a36fc..b82804305a1f3 100644 --- a/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs +++ b/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Runtime.InteropServices; public static class Program { @@ -14,6 +14,7 @@ public static class Program public static async Task Main(string[] args) { mono_ios_set_summary($"Starting functional test"); + Console.WriteLine("Done!"); await Task.Delay(5000); diff --git a/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs index c5221ce4bc3db..93dd7fc3c731f 100644 --- a/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs +++ b/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs @@ -15,10 +15,18 @@ public static class Program public static async Task Main(string[] args) { mono_ios_set_summary($"Starting functional test"); + CultureInfo culture; + try + { + culture = new CultureInfo("es-ES", false); + } + catch + { + culture = new CultureInfo("", false); + } - var culture = new CultureInfo("es-ES", false); // https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#cultures-and-culture-data - int result = culture.LCID == 0x1000 && culture.NativeName == "Invariant Language (Invariant Country)" ? 42 : 1; + int result = culture.LCID == CultureInfo.InvariantCulture.LCID && culture.NativeName == "Invariant Language (Invariant Country)" ? 42 : 1; Console.WriteLine("Done!"); await Task.Delay(5000); diff --git a/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs new file mode 100644 index 0000000000000..df03d367acb37 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +public static class Program +{ + [DllImport("__Internal")] + public static extern void mono_ios_set_summary (string value); + + public static async Task Main(string[] args) + { + mono_ios_set_summary($"Starting functional test"); + + // https://github.com/dotnet/runtime/issues/47112 + var foos = new string [] { "hi", "bye" }; + string f = foos.AsQueryable ().First (); + + Console.WriteLine("Done!"); + await Task.Delay(5000); + + return 42; + } +} diff --git a/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj new file mode 100644 index 0000000000000..b2dc64908e2d0 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj @@ -0,0 +1,19 @@ + + + + Exe + false + true + true + $(NetCoreAppCurrent) + iOSSimulator + iOS.Simulator.LambdaCompilerAot.Test.dll + false + 42 + true + + + + + + From 8b42b7fa31c7c1a55b1b36213db9ef6f562ed61e Mon Sep 17 00:00:00 2001 From: Mansoor Saqib Date: Fri, 9 Jul 2021 07:21:01 -0700 Subject: [PATCH 22/72] Optimize index bounds check for immutable sorted set and list (#53266) * Optimize index bounds check for immutable sorted set and list - The bounds check to determine if a given index is >= 0 and < this.Count is only necessary on the first call to ItemRef. - The recursive steps within ItemRef do not need to continuously do this bounds check on these immutable data structures. - Proof: Elimination of index >= 0 bounds check: The first call to ItemRef checks if index >= 0. If we recurse on the left node, the index value does not change. If we recurse on the right node, index > _left._count. Then index - _left._count - 1 >= 0. Elimination of index < this.Count: The first call to ItemRef checks if index < this.Count. Then the given index must lie somewhere in this tree and (**) index < this.Count == left.Count + right.Count + 1. If we recurse on the left node, the index value does not change and a check is already made to determine that index < _left.Count. If we recurse on the right node, then we need to be sure that index - _left.count - 1 < _right.Count. But this is just a rearrangement of (**). * Remove redundant code * Remove redundant assert * Apply suggestions from code review Change from internal to private for unchecked methods. Co-authored-by: Stephen Toub Co-authored-by: Stephen Toub --- .../Collections/Immutable/ImmutableList_1.Node.cs | 9 +++++++-- .../Collections/Immutable/ImmutableSortedSet_1.Node.cs | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs index ed4ee0a12f036..78480e7d044f2 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs @@ -195,15 +195,20 @@ internal ref readonly T ItemRef(int index) { Requires.Range(index >= 0 && index < this.Count, nameof(index)); + return ref ItemRefUnchecked(index); + } + + private ref readonly T ItemRefUnchecked(int index) + { Debug.Assert(_left != null && _right != null); if (index < _left._count) { - return ref _left.ItemRef(index); + return ref _left.ItemRefUnchecked(index); } if (index > _left._count) { - return ref _right.ItemRef(index - _left._count - 1); + return ref _right.ItemRefUnchecked(index - _left._count - 1); } return ref _key; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs index aea4caff1bea2..c1c53978e4b8c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs @@ -260,16 +260,22 @@ internal T this[int index] internal ref readonly T ItemRef(int index) { Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + return ref ItemRefUnchecked(index); + } + + private ref readonly T ItemRefUnchecked(int index) + { Debug.Assert(_left != null && _right != null); if (index < _left._count) { - return ref _left.ItemRef(index); + return ref _left.ItemRefUnchecked(index); } if (index > _left._count) { - return ref _right.ItemRef(index - _left._count - 1); + return ref _right.ItemRefUnchecked(index - _left._count - 1); } return ref _key; From 784ddf83e79dd0d95664f12e67422ade046345cb Mon Sep 17 00:00:00 2001 From: petrogavriluk Date: Fri, 9 Jul 2021 17:24:27 +0300 Subject: [PATCH 23/72] Fix incorrect comparison (#54834) (#55094) Add test to check fix. Checking comparison result against 1 or -1 causes problems when custom comparer is used. As a result Min and Max properties return incorrect values. --- .../Collections/Generic/SortedSet.TreeSubSet.cs | 4 ++-- .../Generic/SortedSet/SortedSet.Generic.cs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs index 2786b1d4d4e35..945c5b03a43d2 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs @@ -132,7 +132,7 @@ internal override T MinInternal { int comp = _lBoundActive ? Comparer.Compare(_min, current.Item) : -1; - if (comp == 1) + if (comp > 0) { current = current.Right; } @@ -161,7 +161,7 @@ internal override T MaxInternal while (current != null) { int comp = _uBoundActive ? Comparer.Compare(_max, current.Item) : 1; - if (comp == -1) + if (comp < 0) { current = current.Left; } diff --git a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs index 80daed73197cc..5e94a196c74d2 100644 --- a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs +++ b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs @@ -130,6 +130,23 @@ protected override ISet GenericISetFactory() { return new SortedSet(new Comparer_SameAsDefaultComparer()); } + + [Fact] + public void SortedSet_Generic_GetViewBetween_MinMax_WithCustomComparer() + { + var set = (SortedSet)CreateSortedSet(new[] { 5, 15, 25, 35, 45 }, 5, 5); + + for (int i = 0; i <= 40; i += 10) + { + for (int j = i + 10; j <= 50; j += 10) + { + SortedSet view = set.GetViewBetween(i, j); + + Assert.Equal(i + 5, view.Min); + Assert.Equal(j - 5, view.Max); + } + } + } } [OuterLoop] From 7cfa95b98db757288895751b256c5558fdb59e37 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 9 Jul 2021 07:30:28 -0700 Subject: [PATCH 24/72] Shim gss api on Linux to delay loading libgssapi_krb5.so (#55037) * shim one function for starters * do all functions * drop static dependency on libgssapi_krb5.so * remap all gss method calls * keep one lib instance open. * tolerate absence of API in `gss_indicate_mechs`. It may be used for API probing. * init from a static constructor * move the lib name definition to the shim * no indirection * do not try optimizing multiple initialization attempts --- ...terop.NetSecurityNative.IsNtlmInstalled.cs | 24 ++++ .../System.Net.Security.Native/entrypoints.c | 1 + .../extra_libs.cmake | 8 +- .../System.Net.Security.Native/pal_gssapi.c | 111 ++++++++++++++++++ .../System.Net.Security.Native/pal_gssapi.h | 6 + 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs index 79edbf6adad46..24b5b9ad5a78c 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs @@ -10,5 +10,29 @@ internal static partial class NetSecurityNative { [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_IsNtlmInstalled")] internal static extern bool IsNtlmInstalled(); + + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")] + private static extern int EnsureGssInitialized(); + + static NetSecurityNative() + { + GssInitializer.Initialize(); + } + + internal static class GssInitializer + { + static GssInitializer() + { + if (EnsureGssInitialized() != 0) + { + throw new InvalidOperationException(); + } + } + + internal static void Initialize() + { + // No-op that exists to provide a hook for other static constructors. + } + } } } diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c index ac702e9040090..56d2bde8ce6e0 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c @@ -13,6 +13,7 @@ static const Entry s_securityNative[] = DllImportEntry(NetSecurityNative_DeleteSecContext) DllImportEntry(NetSecurityNative_DisplayMajorStatus) DllImportEntry(NetSecurityNative_DisplayMinorStatus) + DllImportEntry(NetSecurityNative_EnsureGssInitialized) DllImportEntry(NetSecurityNative_GetUser) DllImportEntry(NetSecurityNative_ImportPrincipalName) DllImportEntry(NetSecurityNative_ImportUserName) diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake index 49c6ff4961004..0744078474b39 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake @@ -19,5 +19,11 @@ macro(append_extra_security_libs NativeLibsExtra) endif() endif() - list(APPEND ${NativeLibsExtra} ${LIBGSS}) + if(CLR_CMAKE_TARGET_LINUX) + # On Linux libgssapi_krb5.so is loaded on demand to tolerate its absense in singlefile apps that do not use it + list(APPEND ${NativeLibsExtra} dl) + add_definitions(-DGSS_SHIM) + else() + list(APPEND ${NativeLibsExtra} ${LIBGSS}) + endif() endmacro() diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 9628318b773d6..2a37649f56c81 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -20,6 +20,11 @@ #include #include +#if defined(GSS_SHIM) +#include +#include "pal_atomic.h" +#endif + c_static_assert(PAL_GSS_C_DELEG_FLAG == GSS_C_DELEG_FLAG); c_static_assert(PAL_GSS_C_MUTUAL_FLAG == GSS_C_MUTUAL_FLAG); c_static_assert(PAL_GSS_C_REPLAY_FLAG == GSS_C_REPLAY_FLAG); @@ -48,6 +53,103 @@ static gss_OID_desc gss_mech_ntlm_OID_desc = {.length = ARRAY_SIZE(gss_ntlm_oid_ .elements = gss_ntlm_oid_value}; #endif +#if defined(GSS_SHIM) + +#define FOR_ALL_GSS_FUNCTIONS \ + PER_FUNCTION_BLOCK(gss_accept_sec_context) \ + PER_FUNCTION_BLOCK(gss_acquire_cred) \ + PER_FUNCTION_BLOCK(gss_acquire_cred_with_password) \ + PER_FUNCTION_BLOCK(gss_delete_sec_context) \ + PER_FUNCTION_BLOCK(gss_display_name) \ + PER_FUNCTION_BLOCK(gss_display_status) \ + PER_FUNCTION_BLOCK(gss_import_name) \ + PER_FUNCTION_BLOCK(gss_indicate_mechs) \ + PER_FUNCTION_BLOCK(gss_init_sec_context) \ + PER_FUNCTION_BLOCK(gss_inquire_context) \ + PER_FUNCTION_BLOCK(gss_mech_krb5) \ + PER_FUNCTION_BLOCK(gss_oid_equal) \ + PER_FUNCTION_BLOCK(gss_release_buffer) \ + PER_FUNCTION_BLOCK(gss_release_cred) \ + PER_FUNCTION_BLOCK(gss_release_name) \ + PER_FUNCTION_BLOCK(gss_release_oid_set) \ + PER_FUNCTION_BLOCK(gss_unwrap) \ + PER_FUNCTION_BLOCK(gss_wrap) \ + PER_FUNCTION_BLOCK(GSS_C_NT_USER_NAME) \ + PER_FUNCTION_BLOCK(GSS_C_NT_HOSTBASED_SERVICE) + +#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + +#define FOR_ALL_GSS_FUNCTIONS FOR_ALL_GSS_FUNCTIONS \ + PER_FUNCTION_BLOCK(gss_set_cred_option) + +#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + +// define indirection pointers for all functions, like +// static TYPEOF(gss_accept_sec_context)* gss_accept_sec_context_ptr; +#define PER_FUNCTION_BLOCK(fn) \ +static TYPEOF(fn)* fn##_ptr; + +FOR_ALL_GSS_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +static void* volatile s_gssLib = NULL; + +// remap gss function use to use indirection pointers +#define gss_accept_sec_context(...) gss_accept_sec_context_ptr(__VA_ARGS__) +#define gss_acquire_cred(...) gss_acquire_cred_ptr(__VA_ARGS__) +#define gss_acquire_cred_with_password(...) gss_acquire_cred_with_password_ptr(__VA_ARGS__) +#define gss_delete_sec_context(...) gss_delete_sec_context_ptr(__VA_ARGS__) +#define gss_display_name(...) gss_display_name_ptr(__VA_ARGS__) +#define gss_display_status(...) gss_display_status_ptr(__VA_ARGS__) +#define gss_import_name(...) gss_import_name_ptr(__VA_ARGS__) +#define gss_indicate_mechs(...) gss_indicate_mechs_ptr(__VA_ARGS__) +#define gss_init_sec_context(...) gss_init_sec_context_ptr(__VA_ARGS__) +#define gss_inquire_context(...) gss_inquire_context_ptr(__VA_ARGS__) +#define gss_oid_equal(...) gss_oid_equal_ptr(__VA_ARGS__) +#define gss_release_buffer(...) gss_release_buffer_ptr(__VA_ARGS__) +#define gss_release_cred(...) gss_release_cred_ptr(__VA_ARGS__) +#define gss_release_name(...) gss_release_name_ptr(__VA_ARGS__) +#define gss_release_oid_set(...) gss_release_oid_set_ptr(__VA_ARGS__) +#define gss_unwrap(...) gss_unwrap_ptr(__VA_ARGS__) +#define gss_wrap(...) gss_wrap_ptr(__VA_ARGS__) + +#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X +#define gss_set_cred_option(...) gss_set_cred_option_ptr(__VA_ARGS__) +#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + + +#define GSS_C_NT_USER_NAME (*GSS_C_NT_USER_NAME_ptr) +#define GSS_C_NT_HOSTBASED_SERVICE (*GSS_C_NT_HOSTBASED_SERVICE_ptr) +#define gss_mech_krb5 (*gss_mech_krb5_ptr) + +#define gss_lib_name "libgssapi_krb5.so" + +static int32_t ensure_gss_shim_initialized() +{ + void* lib = dlopen(gss_lib_name, RTLD_LAZY); + if (lib == NULL) { fprintf(stderr, "Cannot load library %s \nError: %s\n", gss_lib_name, dlerror()); return -1; } + + // check is someone else has opened and published s_gssLib already + if (!pal_atomic_cas_ptr(&s_gssLib, lib, NULL)) + { + dlclose(lib); + } + + // initialize indirection pointers for all functions, like: + // gss_accept_sec_context_ptr = (TYPEOF(gss_accept_sec_context)*)dlsym(s_gssLib, "gss_accept_sec_context"); + // if (gss_accept_sec_context_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from %s \nError: %s\n", "gss_accept_sec_context", gss_lib_name, dlerror()); return -1; } +#define PER_FUNCTION_BLOCK(fn) \ + fn##_ptr = (TYPEOF(fn)*)dlsym(s_gssLib, #fn); \ + if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from %s \nError: %s\n", gss_lib_name, dlerror()); return -1; } + + FOR_ALL_GSS_FUNCTIONS +#undef PER_FUNCTION_BLOCK + + return 0; +} + +#endif // GSS_SHIM + // transfers ownership of the underlying data from gssBuffer to PAL_GssBuffer static void NetSecurityNative_MoveBuffer(gss_buffer_t gssBuffer, PAL_GssBuffer* targetBuffer) { @@ -567,3 +669,12 @@ uint32_t NetSecurityNative_IsNtlmInstalled() return foundNtlm; } + +int32_t NetSecurityNative_EnsureGssInitialized() +{ +#if defined(GSS_SHIM) + return ensure_gss_shim_initialized(); +#else + return 0; +#endif +} diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 5c1fe55163ec6..b270569c38890 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -193,3 +193,9 @@ Shims gss_inquire_context and gss_display_name to get the remote user principal PALEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, GssCtxId* contextHandle, PAL_GssBuffer* outBuffer); + +/* +Performs initialization of GSS shim, if necessary. +Return value 0 indicates a success. +*/ +PALEXPORT int32_t NetSecurityNative_EnsureGssInitialized(void); From a423f69e61e59eaa64eff348d8838a1226724972 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 9 Jul 2021 10:30:47 -0400 Subject: [PATCH 25/72] Use RetryHelper with AIA tests (#55230) * Use RetryHelper with AIA tests. The AIA tests depend on many external factors including the underlying operation system, network, and the resources available on the system. To accomodate this we will re-run the tests to improve reliability. * Narrow the use of RetryHelper --- .../tests/RevocationTests/AiaTests.cs | 130 ++++++++++-------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index 0a2e8b78cffce..88cf0c20b8c6b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -29,22 +29,26 @@ public static void EmptyAiaResponseIsIgnored() using (CertificateAuthority intermediate1 = intermediates[0]) using (CertificateAuthority intermediate2 = intermediates[1]) using (endEntity) - using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 intermediate2Cert = intermediate2.CloneIssuerCert()) { responder.RespondEmpty = true; - X509Chain chain = holder.Chain; - chain.ChainPolicy.ExtraStore.Add(intermediate2Cert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - - Assert.False(chain.Build(endEntity)); - X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); - Assert.True(chainFlags.HasFlag(X509ChainStatusFlags.PartialChain), $"expected partial chain flags, got {chainFlags}"); - Assert.Equal(2, chain.ChainElements.Count); + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + { + X509Chain chain = holder.Chain; + chain.ChainPolicy.ExtraStore.Add(intermediate2Cert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + + Assert.False(chain.Build(endEntity)); + X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); + Assert.True(chainFlags.HasFlag(X509ChainStatusFlags.PartialChain), $"expected partial chain flags, got {chainFlags}"); + Assert.Equal(2, chain.ChainElements.Count); + } + }); } } @@ -65,72 +69,76 @@ public static void DisableAiaOptionWorks() using (root) using (intermediate) using (endEntity) - using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = root.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediate.CloneIssuerCert()) - using (var cuCaStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) { - cuCaStore.Open(OpenFlags.ReadWrite); - - X509Chain chain = holder.Chain; + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + using (var cuCaStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) + { + cuCaStore.Open(OpenFlags.ReadWrite); - // macOS combines revocation and AIA fetching in to a single flag. Both need to be disabled - // to prevent AIA fetches. - if (PlatformDetection.IsOSX) - { - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - } + X509Chain chain = holder.Chain; - chain.ChainPolicy.DisableCertificateDownloads = true; - chain.ChainPolicy.CustomTrustStore.Add(rootCert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; + // macOS combines revocation and AIA fetching in to a single flag. Both need to be disabled + // to prevent AIA fetches. + if (PlatformDetection.IsOSX) + { + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + } - Assert.False(chain.Build(endEntity), "Chain build with no intermediate, AIA disabled"); + chain.ChainPolicy.DisableCertificateDownloads = true; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; - // If a previous run of this test leaves contamination in the CU\CA store on Windows - // the Windows chain engine will match the bad issuer and report NotSignatureValid instead - // of PartialChain. - X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); + Assert.False(chain.Build(endEntity), "Chain build with no intermediate, AIA disabled"); - if (chainFlags.HasFlag(X509ChainStatusFlags.NotSignatureValid)) - { - Assert.Equal(3, chain.ChainElements.Count); + // If a previous run of this test leaves contamination in the CU\CA store on Windows + // the Windows chain engine will match the bad issuer and report NotSignatureValid instead + // of PartialChain. + X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); - foreach (X509Certificate2 storeCert in cuCaStore.Certificates) - { - if (storeCert.Subject.Equals(intermediateCert.Subject)) + if (chainFlags.HasFlag(X509ChainStatusFlags.NotSignatureValid)) { - cuCaStore.Remove(storeCert); - } + Assert.Equal(3, chain.ChainElements.Count); - storeCert.Dispose(); - } + foreach (X509Certificate2 storeCert in cuCaStore.Certificates) + { + if (storeCert.Subject.Equals(intermediateCert.Subject)) + { + cuCaStore.Remove(storeCert); + } - holder.DisposeChainElements(); + storeCert.Dispose(); + } - // Try again, with no caching side effect. - Assert.False(chain.Build(endEntity), "Chain build 2 with no intermediate, AIA disabled"); - } + holder.DisposeChainElements(); - Assert.Equal(1, chain.ChainElements.Count); - Assert.Contains(X509ChainStatusFlags.PartialChain, chain.ChainStatus.Select(s => s.Status)); - holder.DisposeChainElements(); + // Try again, with no caching side effect. + Assert.False(chain.Build(endEntity), "Chain build 2 with no intermediate, AIA disabled"); + } + + Assert.Equal(1, chain.ChainElements.Count); + Assert.Contains(X509ChainStatusFlags.PartialChain, chain.ChainStatus.Select(s => s.Status)); + holder.DisposeChainElements(); - chain.ChainPolicy.ExtraStore.Add(intermediateCert); - Assert.True(chain.Build(endEntity), "Chain build with intermediate, AIA disabled"); - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); - holder.DisposeChainElements(); + chain.ChainPolicy.ExtraStore.Add(intermediateCert); + Assert.True(chain.Build(endEntity), "Chain build with intermediate, AIA disabled"); + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); + holder.DisposeChainElements(); - chain.ChainPolicy.DisableCertificateDownloads = false; - chain.ChainPolicy.ExtraStore.Clear(); - Assert.True(chain.Build(endEntity), "Chain build with no intermediate, AIA enabled"); - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); + chain.ChainPolicy.DisableCertificateDownloads = false; + chain.ChainPolicy.ExtraStore.Clear(); + Assert.True(chain.Build(endEntity), "Chain build with no intermediate, AIA enabled"); + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); - cuCaStore.Remove(intermediateCert); + cuCaStore.Remove(intermediateCert); + } + }); } } } From e4ca022605758b914ca7202d26d6516eb25167ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 9 Jul 2021 17:04:39 +0200 Subject: [PATCH 26/72] Fix compiling System.Net.Security.Native with HAVE_HEIMDAL_HEADERS (#54682) We are using malloc/free but don't declare a header. It likely happens to be included from GSS/GSS.h which is why it builds now, but not from the Heimdal headers. --- .../Native/Unix/System.Net.Security.Native/pal_gssapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 2a37649f56c81..d5b0e0bcdda1b 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -19,6 +19,7 @@ #include #include +#include #if defined(GSS_SHIM) #include From 03eb03e5754f52328c9450439736d276dc591e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 9 Jul 2021 11:37:28 -0400 Subject: [PATCH 27/72] Build all tasks, regardless of target (#55346) In a dirty tree, different architectures require different tasks. For example, ``` ./build.sh --os iossimulator -c Release ./build.sh --os android -c Release ``` the second step will complain that the AndroidAppBuilder task does not exist, because the build-semaphore.txt was created during the first build which built the AppleAppBuilder, but not the AndroidAppBuilder. The solution is to build all the tasks, regardless of target Simplifies building for different targets from the same dirty tree. All the tasks are always built once and then remain unchanged as long as their sources don't change. --- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 2 +- src/tasks/tasks.proj | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 9bd0cd7d0e765..0b20d27f38835 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -161,7 +161,7 @@ public override bool Execute() throw new ArgumentException($"MainLibraryFileName='{MainLibraryFileName}' was not found in AppDir='{AppDir}'"); } - if (ProjectName.Contains(" ")) + if (ProjectName.Contains(' ')) { throw new ArgumentException($"ProjectName='{ProjectName}' should not contain spaces"); } diff --git a/src/tasks/tasks.proj b/src/tasks/tasks.proj index e681cfd2740dd..9c62f8b76b989 100644 --- a/src/tasks/tasks.proj +++ b/src/tasks/tasks.proj @@ -1,18 +1,6 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -8044,6 +8968,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst index 285e9101c6321..4ac4fe405d9da 100644 --- a/src/coreclr/vm/ClrEtwAllMeta.lst +++ b/src/coreclr/vm/ClrEtwAllMeta.lst @@ -633,3 +633,33 @@ nomac:StressLogTask:::StressLogEvent_V1 # StackWalk events ################## nomac:CLRStackStress:::CLRStackWalkStress + +################################# +# Events from the Mono profiler provider +################################# +nostack::MonoProfiler::ExceptionClause +nostack::MonoProfiler::MonoProfilerMethodEnter +nostack::MonoProfiler::MonoProfilerMethodLeave +nostack::MonoProfiler::MonoProfilerMethodTailCall +nostack::MonoProfiler::MonoProfilerMethodExceptionLeave +nostack::MonoProfiler::MonoProfilerMethodFree +nostack::MonoProfiler::MonoProfilerMethodBeginInvoke +nostack::MonoProfiler::MonoProfilerMethodEndInvoke +nostack::MonoProfiler::MonoProfilerGCEvent +nostack::MonoProfiler::MonoProfilerGCMoves +nostack::MonoProfiler::MonoProfilerGCResize +nostack::MonoProfiler::MonoProfilerGCFinalizing +nostack::MonoProfiler::MonoProfilerGCFinalized +nostack::MonoProfiler::MonoProfilerGCFinalizingObject +nostack::MonoProfiler::MonoProfilerGCFinalizedObject +nostack::MonoProfiler::MonoProfilerGCRootRegister +nostack::MonoProfiler::MonoProfilerGCRootUnregister +nostack::MonoProfiler::MonoProfilerGCRoots +nostack::MonoProfiler::MonoProfilerGCHeapDumpStart +nostack::MonoProfiler::MonoProfilerGCHeapDumpStop +nostack::MonoProfiler::MonoProfilerGCHeapDumpObjectReference +nostack::MonoProfiler::MonoProfilerThreadStarted +nostack::MonoProfiler::MonoProfilerThreadStopping +nostack::MonoProfiler::MonoProfilerThreadStopped +nostack::MonoProfiler::MonoProfilerThreadExited +nostack::MonoProfiler::MonoProfilerThreadName \ No newline at end of file diff --git a/src/mono/mono/component/event_pipe.c b/src/mono/mono/component/event_pipe.c index 6ed6171875a03..24aa0952ce7aa 100644 --- a/src/mono/mono/component/event_pipe.c +++ b/src/mono/mono/component/event_pipe.c @@ -11,6 +11,8 @@ #include #include +static bool _event_pipe_component_inited = false; + struct _EventPipeProviderConfigurationNative { gunichar2 *provider_name; uint64_t keywords; @@ -284,5 +286,11 @@ event_pipe_thread_ctrl_activity_id ( MonoComponentEventPipe * mono_component_event_pipe_init (void) { + if (!_event_pipe_component_inited) { + extern void ep_rt_mono_component_init (void); + ep_rt_mono_component_init (); + _event_pipe_component_inited = true; + } + return &fn_table; } diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 9a83d80061bf5..683d94a61bb12 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -16,11 +16,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include "mono/utils/mono-logger-internals.h" #include #include @@ -49,8 +53,11 @@ char *_ep_rt_mono_managed_cmd_line = NULL; static GArray * _ep_rt_mono_sampled_thread_callstacks = NULL; static uint32_t _ep_rt_mono_max_sampled_thread_count = 32; -// Mono profiler. -static MonoProfilerHandle _ep_rt_mono_profiler = NULL; +// Mono profilers. +static MonoProfilerHandle _ep_rt_default_profiler = NULL; +static MonoProfilerHandle _ep_rt_dotnet_runtime_profiler_provider = NULL; +static MonoProfilerHandle _ep_rt_dotnet_mono_profiler_provider = NULL; +static MonoCallSpec _ep_rt_dotnet_mono_profiler_provider_callspec = {0}; // Phantom JIT compile method. MonoMethod *_ep_rt_mono_runtime_helper_compile_method = NULL; @@ -196,6 +203,38 @@ typedef struct _AssemblyEventData AssemblyEventData; #define EXCEPTION_THROWN_FLAGS_IS_CSE 0x8 #define EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT 0x10 +// Provider keyword flags. +#define GC_KEYWORD 0x1 +#define GC_HANDLE_KEYWORD 0x2 +#define LOADER_KEYWORD 0x8 +#define JIT_KEYWORD 0x10 +#define APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD 0x800 +#define CONTENTION_KEYWORD 0x4000 +#define EXCEPTION_KEYWORD 0x8000 +#define THREADING_KEYWORD 0x10000 +#define GC_ALLOCATION_KEYWORD 0x200000 +#define GC_MOVES_KEYWORD 0x400000 +#define GC_ROOT_KEYWORD 0x800000 +#define GC_FINALIZATION_KEYWORD 0x1000000 +#define GC_RESIZE_KEYWORD 0x2000000 +#define METHOD_TRACING_KEYWORD 0x20000000 +#define TYPE_DIAGNOSTIC_KEYWORD 0x8000000000 +#define TYPE_LOADING_KEYWORD 0x8000000000 +#define MONITOR_KEYWORD 0x10000000000 +#define METHOD_INSTRUMENTATION_KEYWORD 0x40000000000 + +// GC provider types. + +typedef struct _GCObjectAddressData { + MonoObject *object; + void *address; +} GCObjectAddressData; + +typedef struct _GCAddressObjectData { + void *address; + MonoObject *object; +} GCAddressObjectData; + /* * Forward declares of all static functions. */ @@ -303,6 +342,10 @@ profiler_eventpipe_thread_exited ( MonoProfiler *prof, uintptr_t tid); +static +bool +parse_mono_profiler_options (const ep_char8_t *option); + static bool get_module_event_data ( @@ -328,86 +371,86 @@ get_exception_ip_func ( static void -profiler_jit_begin ( +runtime_profiler_jit_begin ( MonoProfiler *prof, MonoMethod *method); static void -profiler_jit_failed ( +runtime_profiler_jit_failed ( MonoProfiler *prof, MonoMethod *method); static void -profiler_jit_done ( +runtime_profiler_jit_done ( MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji); static void -profiler_image_loaded ( +runtime_profiler_image_loaded ( MonoProfiler *prof, MonoImage *image); static void -profiler_image_unloaded ( +runtime_profiler_image_unloaded ( MonoProfiler *prof, MonoImage *image); static void -profiler_assembly_loaded ( +runtime_profiler_assembly_loaded ( MonoProfiler *prof, MonoAssembly *assembly); static void -profiler_assembly_unloaded ( +runtime_profiler_assembly_unloaded ( MonoProfiler *prof, MonoAssembly *assembly); static void -profiler_thread_started ( +runtime_profiler_thread_started ( MonoProfiler *prof, uintptr_t tid); static void -profiler_thread_stopped ( +runtime_profiler_thread_stopped ( MonoProfiler *prof, uintptr_t tid); static void -profiler_class_loading ( +runtime_profiler_class_loading ( MonoProfiler *prof, MonoClass *klass); static void -profiler_class_failed ( +runtime_profiler_class_failed ( MonoProfiler *prof, MonoClass *klass); static void -profiler_class_loaded ( +runtime_profiler_class_loaded ( MonoProfiler *prof, MonoClass *klass); static void -profiler_exception_throw ( +runtime_profiler_exception_throw ( MonoProfiler *prof, MonoObject *exception); static void -profiler_exception_clause ( +runtime_profiler_exception_clause ( MonoProfiler *prof, MonoMethod *method, uint32_t clause_num, @@ -416,2571 +459,4443 @@ profiler_exception_clause ( static void -profiler_monitor_contention ( +runtime_profiler_monitor_contention ( MonoProfiler *prof, MonoObject *obj); static void -profiler_monitor_acquired ( +runtime_profiler_monitor_acquired ( MonoProfiler *prof, MonoObject *obj); static void -profiler_monitor_failed ( +runtime_profiler_monitor_failed ( MonoProfiler *prof, MonoObject *obj); static void -profiler_jit_code_buffer ( +runtime_profiler_jit_code_buffer ( MonoProfiler *prof, const mono_byte *buffer, uint64_t size, MonoProfilerCodeBufferType type, const void *data); -/* - * Forward declares of all private functions (accessed using extern in ep-rt-mono.h). - */ +static +void +mono_profiler_app_domain_loading ( + MonoProfiler *prof, + MonoDomain *domain); +static void -ep_rt_mono_init (void); +mono_profiler_app_domain_loaded ( + MonoProfiler *prof, + MonoDomain *domain); +static void -ep_rt_mono_init_finish (void); +mono_profiler_app_domain_unloading ( + MonoProfiler *prof, + MonoDomain *domain); +static void -ep_rt_mono_fini (void); +mono_profiler_app_domain_unloaded ( + MonoProfiler *prof, + MonoDomain *domain); -bool -ep_rt_mono_rand_try_get_bytes ( - uint8_t *buffer, - size_t buffer_size); +static +void +mono_profiler_app_domain_name ( + MonoProfiler *prof, + MonoDomain *domain, + const char *name); -EventPipeThread * -ep_rt_mono_thread_get_or_create (void); +static +void +mono_profiler_jit_begin ( + MonoProfiler *prof, + MonoMethod *method); -void * -ep_rt_mono_thread_attach (bool background_thread); +static +void +mono_profiler_jit_failed ( + MonoProfiler *prof, + MonoMethod *method); -void * -ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); +static +void +mono_profiler_jit_done ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji); +static void -ep_rt_mono_thread_detach (void); +mono_profiler_jit_chunk_created ( + MonoProfiler *prof, + const mono_byte *chunk, + uintptr_t size); +static void -ep_rt_mono_thread_exited (void); +mono_profiler_jit_chunk_destroyed ( + MonoProfiler *prof, + const mono_byte *chunk); -int64_t -ep_rt_mono_perf_counter_query (void); +static +void +mono_profiler_jit_code_buffer ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data); -int64_t -ep_rt_mono_perf_frequency_query (void); +static +void +mono_profiler_class_loading ( + MonoProfiler *prof, + MonoClass *klass); +static void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time); +mono_profiler_class_failed ( + MonoProfiler *prof, + MonoClass *klass); -int64_t -ep_rt_mono_system_timestamp_get (void); +static +void +mono_profiler_class_loaded ( + MonoProfiler *prof, + MonoClass *klass); +static void -ep_rt_mono_os_environment_get_utf16 (ep_rt_env_array_utf16_t *env_array); +mono_profiler_vtable_loading ( + MonoProfiler *prof, + MonoVTable *vtable); +static void -ep_rt_mono_init_providers_and_events (void); +mono_profiler_vtable_failed ( + MonoProfiler *prof, + MonoVTable *vtable); +static void -ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config); +mono_profiler_vtable_loaded ( + MonoProfiler *prof, + MonoVTable *vtable); -bool -ep_rt_mono_providers_validate_all_disabled (void); +static +void +mono_profiler_module_loading ( + MonoProfiler *prof, + MonoImage *image); +static void -ep_rt_mono_fini_providers_and_events (void); +mono_profiler_module_failed ( + MonoProfiler *prof, + MonoImage *image); -bool -ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( - ep_rt_thread_handle_t sampling_thread, - EventPipeEvent *sampling_event); +static +void +mono_profiler_module_loaded ( + MonoProfiler *prof, + MonoImage *image); -bool -ep_rt_mono_walk_managed_stack_for_thread ( - ep_rt_thread_handle_t thread, - EventPipeStackContents *stack_contents); +static +void +mono_profiler_module_unloading ( + MonoProfiler *prof, + MonoImage *image); -bool -ep_rt_mono_method_get_simple_assembly_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len); +static +void +mono_profiler_module_unloaded ( + MonoProfiler *prof, + MonoImage *image); -bool -ep_rt_mono_method_get_full_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len); +static +void +mono_profiler_assembly_loading ( + MonoProfiler *prof, + MonoAssembly *assembly); +static void -ep_rt_mono_execute_rundown (ep_rt_execution_checkpoint_array_t *execution_checkpoints); +mono_profiler_assembly_loaded ( + MonoProfiler *prof, + MonoAssembly *assembly); static -inline -uint16_t -clr_instance_get_id (void) -{ - // Mono runtime id. - return 9; -} +void +mono_profiler_assembly_unloading ( + MonoProfiler *prof, + MonoAssembly *assembly); static -bool -fire_method_rundown_events_func ( - const uint64_t method_id, - const uint64_t module_id, - const uint64_t method_start_address, - const uint32_t method_size, - const uint32_t method_token, - const uint32_t method_flags, - const ep_char8_t *method_namespace, - const ep_char8_t *method_name, - const ep_char8_t *method_signature, - const uint16_t count_of_map_entries, - const uint32_t *il_offsets, - const uint32_t *native_offsets, - bool aot_method, - bool verbose, - void *user_data) -{ - FireEtwMethodDCEndILToNativeMap ( - method_id, - 0, - 0, - count_of_map_entries, - il_offsets, - native_offsets, - clr_instance_get_id (), - NULL, - NULL); +void +mono_profiler_assembly_unloaded ( + MonoProfiler *prof, + MonoAssembly *assembly); - if (verbose) { - FireEtwMethodDCEndVerbose_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_method_enter ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context); - if (aot_method) - FireEtwMethodDCEndVerbose_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - } else { - FireEtwMethodDCEnd_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_method_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context); - if (aot_method) - FireEtwMethodDCEnd_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - clr_instance_get_id (), - NULL, - NULL); - } +static +void +mono_profiler_method_tail_call ( + MonoProfiler *prof, + MonoMethod *method, + MonoMethod *target_method); - return true; -} +static +void +mono_profiler_method_exception_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoObject *exc); static -bool -fire_assembly_rundown_events_func ( - const uint64_t domain_id, - const uint64_t assembly_id, - const uint32_t assembly_flags, - const uint32_t binding_id, - const ep_char8_t *assembly_name, - const uint64_t module_id, - const uint32_t module_flags, - const uint32_t reserved_flags, - const ep_char8_t *module_il_path, - const ep_char8_t *module_native_path, - const uint8_t *managed_pdb_signature, - const uint32_t managed_pdb_age, - const ep_char8_t *managed_pdb_build_path, - const uint8_t *native_pdb_signature, - const uint32_t native_pdb_age, - const ep_char8_t *native_pdb_build_path, - void *user_data) -{ - FireEtwModuleDCEnd_V2 ( - module_id, - assembly_id, - module_flags, - reserved_flags, - module_il_path, - module_native_path, - clr_instance_get_id (), - managed_pdb_signature, - managed_pdb_age, - managed_pdb_build_path, - native_pdb_signature, - native_pdb_age, - native_pdb_build_path, - NULL, - NULL); +void +mono_profiler_method_free ( + MonoProfiler *prof, + MonoMethod *method); - FireEtwDomainModuleDCEnd_V1 ( - module_id, - assembly_id, - domain_id, - module_flags, - reserved_flags, - module_il_path, - module_native_path, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_method_begin_invoke ( + MonoProfiler *prof, + MonoMethod *method); - FireEtwAssemblyDCEnd_V1 ( - assembly_id, - domain_id, - binding_id, - assembly_flags, - assembly_name, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_method_end_invoke ( + MonoProfiler *prof, + MonoMethod *method); - return true; -} +static +MonoProfilerCallInstrumentationFlags +mono_profiler_method_instrumentation ( + MonoProfiler *prof, + MonoMethod *method); static -bool -fire_domain_rundown_events_func ( - const uint64_t domain_id, - const uint32_t domain_flags, - const ep_char8_t *domain_name, - const uint32_t domain_index, - void *user_data) -{ - return FireEtwAppDomainDCEnd_V1 ( - domain_id, - domain_flags, - domain_name, - domain_index, - clr_instance_get_id (), - NULL, - NULL); -} +void +mono_profiler_exception_throw ( + MonoProfiler *prof, + MonoObject *exc); static void -eventpipe_fire_method_events ( - MonoJitInfo *ji, +mono_profiler_exception_clause ( + MonoProfiler *prof, MonoMethod *method, - EventPipeFireMethodEventsData *events_data) -{ - EP_ASSERT (ji != NULL); - EP_ASSERT (events_data->domain != NULL); - EP_ASSERT (events_data->method_events_func != NULL); - - uint64_t method_id = 0; - uint64_t module_id = 0; - uint64_t method_code_start = (uint64_t)ji->code_start; - uint32_t method_code_size = (uint32_t)ji->code_size; - uint32_t method_token = 0; - uint32_t method_flags = 0; - uint8_t kind = MONO_CLASS_DEF; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc); - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. +static +void +mono_profiler_gc_event ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial); - if (method) { - method_id = (uint64_t)method; - method_token = method->token; +static +void +mono_profiler_gc_allocation ( + MonoProfiler *prof, + MonoObject *object); - if (mono_jit_info_get_generic_sharing_context (ji)) - method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; +static +void +mono_profiler_gc_moves ( + MonoProfiler *prof, + MonoObject *const* objects, + uint64_t count); - if (method->dynamic) - method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; +static +void +mono_profiler_gc_resize ( + MonoProfiler *prof, + uintptr_t size); - if (!ji->from_aot && !ji->from_llvm) { - method_flags |= METHOD_FLAGS_JITTED_METHOD; - if (method->wrapper_type != MONO_WRAPPER_NONE) - method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; - } +static +void +mono_profiler_gc_handle_created ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type, + MonoObject * object); - if (method->is_generic || method->is_inflated) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; +static +void +mono_profiler_gc_handle_deleted ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type); - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - kind = m_class_get_class_kind (method->klass); - if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - } +static +void +mono_profiler_gc_finalizing (MonoProfiler *prof); - if (verbose) { - method_name = method->name; - method_signature = mono_signature_full_name (method->signature); - if (method->klass) - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); - } +static +void +mono_profiler_gc_finalized (MonoProfiler *prof); - } +static +void +mono_profiler_gc_root_register ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name); - uint16_t offset_entries = 0; - uint32_t *il_offsets = NULL; - uint32_t *native_offsets = NULL; +static +void +mono_profiler_gc_root_unregister ( + MonoProfiler *prof, + const mono_byte *start); - MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, events_data->domain) : NULL; - if (debug_info) { - offset_entries = debug_info->num_line_numbers; - if (offset_entries != 0) { - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (!events_data->buffer || needed_size > events_data->buffer_size) { - g_free (events_data->buffer); - events_data->buffer_size = (size_t)(needed_size * 1.5); - events_data->buffer = g_new (uint8_t, events_data->buffer_size); - } +static +void +mono_profiler_gc_roots ( + MonoProfiler *prof, + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects); - if (events_data->buffer) { - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; +static +void +mono_profiler_monitor_contention ( + MonoProfiler *prof, + MonoObject *object); - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; - } - } - } +static +void +mono_profiler_monitor_failed ( + MonoProfiler *prof, + MonoObject *object); - mono_debug_free_method_jit_info (debug_info); - } +static +void +mono_profiler_monitor_acquired ( + MonoProfiler *prof, + MonoObject *object); - if (events_data->buffer && !il_offsets && !native_offsets) { - // No IL offset -> Native offset mapping available. Put all code on IL offset 0. - EP_ASSERT (events_data->buffer_size >= sizeof (uint32_t) * 2); - offset_entries = 1; - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; - il_offsets [0] = 0; - native_offsets [0] = (uint32_t)ji->code_size; - } +static +void +mono_profiler_thread_started ( + MonoProfiler *prof, + uintptr_t tid); - events_data->method_events_func ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags, - (ep_char8_t *)method_namespace, - (ep_char8_t *)method_name, - (ep_char8_t *)method_signature, - offset_entries, - il_offsets, - native_offsets, - (ji->from_aot || ji->from_llvm), - verbose, - NULL); +static +void +mono_profiler_thread_stopping ( + MonoProfiler *prof, + uintptr_t tid); - g_free (method_namespace); - g_free (method_signature); -} +static +void +mono_profiler_thread_stopped ( + MonoProfiler *prof, + uintptr_t tid); static -inline -bool -include_method (MonoMethod *method) -{ - if (!method) { - return false; - } else if (!m_method_is_wrapper (method)) { - return true; - } else { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); - return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; - } -} +void +mono_profiler_thread_exited ( + MonoProfiler *prof, + uintptr_t tid); static void -eventpipe_fire_method_events_func ( - MonoJitInfo *ji, - void *user_data) -{ - EventPipeFireMethodEventsData *events_data = (EventPipeFireMethodEventsData *)user_data; - EP_ASSERT (events_data != NULL); +mono_profiler_thread_name ( + MonoProfiler *prof, + uintptr_t tid, + const char *name); - if (ji && !ji->is_trampoline && !ji->async) { - MonoMethod *method = jinfo_get_method (ji); - if (include_method (method)) - eventpipe_fire_method_events (ji, method, events_data); - } -} +/* + * Forward declares of all private functions (accessed using extern in ep-rt-mono.h). + */ -static void -eventpipe_fire_assembly_events ( - MonoDomain *domain, - MonoAssembly *assembly, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func) -{ - EP_ASSERT (domain != NULL); - EP_ASSERT (assembly != NULL); - EP_ASSERT (assembly_events_func != NULL); - - uint64_t domain_id = (uint64_t)domain; - uint64_t module_id = (uint64_t)assembly->image; - uint64_t assembly_id = (uint64_t)assembly; - - // TODO: Extract all module IL/Native paths and pdb metadata when available. - const char *module_il_path = ""; - const char *module_il_pdb_path = ""; - const char *module_native_path = ""; - const char *module_native_pdb_path = ""; - uint8_t signature [EP_GUID_SIZE] = { 0 }; - uint32_t module_il_pdb_age = 0; - uint32_t module_native_pdb_age = 0; +ep_rt_mono_component_init (void); - uint32_t reserved_flags = 0; - uint64_t binding_id = 0; +void +ep_rt_mono_init (void); - // Native methods are part of JIT table and already emitted. - // TODO: FireEtwMethodDCEndVerbose_V1_or_V2 for all native methods in module as well? +void +ep_rt_mono_init_finish (void); - // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. - uint32_t module_flags = MODULE_FLAGS_MANIFEST_MODULE; - if (assembly->image) { - if (assembly->image->dynamic) - module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; - if (assembly->image->aot_module) - module_flags |= MODULE_FLAGS_NATIVE_MODULE; +void +ep_rt_mono_fini (void); - module_il_path = assembly->image->filename ? assembly->image->filename : ""; - } +bool +ep_rt_mono_rand_try_get_bytes ( + uint8_t *buffer, + size_t buffer_size); - uint32_t assembly_flags = 0; - if (assembly->dynamic) - assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; +EventPipeThread * +ep_rt_mono_thread_get_or_create (void); - if (assembly->image && assembly->image->aot_module) { - assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; - } +void * +ep_rt_mono_thread_attach (bool background_thread); - char *assembly_name = mono_stringify_assembly_name (&assembly->aname); +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); - assembly_events_func ( - domain_id, - assembly_id, - assembly_flags, - binding_id, - (const ep_char8_t*)assembly_name, - module_id, - module_flags, - reserved_flags, - (const ep_char8_t *)module_il_path, - (const ep_char8_t *)module_native_path, - signature, - module_il_pdb_age, - (const ep_char8_t *)module_il_pdb_path, - signature, - module_native_pdb_age, - (const ep_char8_t *)module_native_pdb_path, - NULL); +void +ep_rt_mono_thread_detach (void); - g_free (assembly_name); -} +void +ep_rt_mono_thread_exited (void); -static -gboolean -eventpipe_execute_rundown ( - ep_rt_mono_fire_domain_rundown_events_func domain_events_func, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func, - ep_rt_mono_fire_method_rundown_events_func method_events_func) -{ - EP_ASSERT (domain_events_func != NULL); - EP_ASSERT (assembly_events_func != NULL); - EP_ASSERT (method_events_func != NULL); +int64_t +ep_rt_mono_perf_counter_query (void); - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); - if (root_domain) { - uint64_t domain_id = (uint64_t)root_domain; +int64_t +ep_rt_mono_perf_frequency_query (void); - // Emit all functions in use (JIT, AOT and Interpreter). - EventPipeFireMethodEventsData events_data; - events_data.domain = root_domain; - events_data.buffer_size = 1024 * sizeof(uint32_t); - events_data.buffer = g_new (uint8_t, events_data.buffer_size); - events_data.method_events_func = method_events_func; +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time); - // All called JIT/AOT methods should be included in jit info table. - mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); +int64_t +ep_rt_mono_system_timestamp_get (void); - // All called interpreted methods should be included in interpreter jit info table. - if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) - mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); +void +ep_rt_mono_os_environment_get_utf16 (ep_rt_env_array_utf16_t *env_array); - // Phantom methods injected in callstacks representing runtime functions. - if (_ep_rt_mono_runtime_helper_compile_method_jitinfo && _ep_rt_mono_runtime_helper_compile_method) - eventpipe_fire_method_events (_ep_rt_mono_runtime_helper_compile_method_jitinfo, _ep_rt_mono_runtime_helper_compile_method, &events_data); - if (_ep_rt_mono_monitor_enter_method_jitinfo && _ep_rt_mono_monitor_enter_method) - eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_method_jitinfo, _ep_rt_mono_monitor_enter_method, &events_data); - if (_ep_rt_mono_monitor_enter_v4_method_jitinfo && _ep_rt_mono_monitor_enter_v4_method) - eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_v4_method_jitinfo, _ep_rt_mono_monitor_enter_v4_method, &events_data); +void +ep_rt_mono_init_providers_and_events (void); - g_free (events_data.buffer); +void +ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config); - // Iterate all assemblies in domain. - GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); - if (assemblies) { - for (int i = 0; i < assemblies->len; ++i) { - MonoAssembly *assembly = (MonoAssembly *)g_ptr_array_index (assemblies, i); - if (assembly) - eventpipe_fire_assembly_events (root_domain, assembly, assembly_events_func); - } - g_ptr_array_free (assemblies, TRUE); - } +bool +ep_rt_mono_providers_validate_all_disabled (void); - uint32_t domain_flags = DOMAIN_FLAGS_DEFAULT_DOMAIN | DOMAIN_FLAGS_EXECUTABLE_DOMAIN; - const char *domain_name = root_domain->friendly_name ? root_domain->friendly_name : ""; - uint32_t domain_index = 1; +void +ep_rt_mono_fini_providers_and_events (void); - domain_events_func ( - domain_id, - domain_flags, - (const ep_char8_t *)domain_name, - domain_index, - NULL); - } +bool +ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( + ep_rt_thread_handle_t sampling_thread, + EventPipeEvent *sampling_event); - return TRUE; -} +bool +ep_rt_mono_walk_managed_stack_for_thread ( + ep_rt_thread_handle_t thread, + EventPipeStackContents *stack_contents); -inline -static bool -in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) -{ - EP_ASSERT (stack_content != NULL); +ep_rt_mono_method_get_simple_assembly_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len); - // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. - if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) - return true; +bool +ep_rt_mono_method_get_full_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len); - return false; -} +void +ep_rt_mono_execute_rundown (ep_rt_execution_checkpoint_array_t *execution_checkpoints); -inline static +inline bool -in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +profiler_callback_is_enabled (uint64_t enabled_keywords, uint64_t keyword) { - EP_ASSERT (stack_content != NULL); - - // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. - if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && - (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) - return true; - - return false; + return (enabled_keywords & keyword) == keyword; } -inline static -bool -in_monitor_enter_frame (WrapperInfo *wrapper) +inline +uint16_t +clr_instance_get_id (void) { - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) - return true; - - return false; + // Mono runtime id. + return 9; } -inline static bool -in_monitor_enter_v4_frame (WrapperInfo *wrapper) +fire_method_rundown_events_func ( + const uint64_t method_id, + const uint64_t module_id, + const uint64_t method_start_address, + const uint32_t method_size, + const uint32_t method_token, + const uint32_t method_flags, + const ep_char8_t *method_namespace, + const ep_char8_t *method_name, + const ep_char8_t *method_signature, + const uint16_t count_of_map_entries, + const uint32_t *il_offsets, + const uint32_t *native_offsets, + bool aot_method, + bool verbose, + void *user_data) { - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) - return true; - - return false; -} - -static + FireEtwMethodDCEndILToNativeMap ( + method_id, + 0, + 0, + count_of_map_entries, + il_offsets, + native_offsets, + clr_instance_get_id (), + NULL, + NULL); + + if (verbose) { + FireEtwMethodDCEndVerbose_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + if (aot_method) + FireEtwMethodDCEndVerbose_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + } else { + FireEtwMethodDCEnd_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + clr_instance_get_id (), + NULL, + NULL); + + if (aot_method) + FireEtwMethodDCEnd_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + clr_instance_get_id (), + NULL, + NULL); + } + + return true; +} + +static +bool +fire_assembly_rundown_events_func ( + const uint64_t domain_id, + const uint64_t assembly_id, + const uint32_t assembly_flags, + const uint32_t binding_id, + const ep_char8_t *assembly_name, + const uint64_t module_id, + const uint32_t module_flags, + const uint32_t reserved_flags, + const ep_char8_t *module_il_path, + const ep_char8_t *module_native_path, + const uint8_t *managed_pdb_signature, + const uint32_t managed_pdb_age, + const ep_char8_t *managed_pdb_build_path, + const uint8_t *native_pdb_signature, + const uint32_t native_pdb_age, + const ep_char8_t *native_pdb_build_path, + void *user_data) +{ + FireEtwModuleDCEnd_V2 ( + module_id, + assembly_id, + module_flags, + reserved_flags, + module_il_path, + module_native_path, + clr_instance_get_id (), + managed_pdb_signature, + managed_pdb_age, + managed_pdb_build_path, + native_pdb_signature, + native_pdb_age, + native_pdb_build_path, + NULL, + NULL); + + FireEtwDomainModuleDCEnd_V1 ( + module_id, + assembly_id, + domain_id, + module_flags, + reserved_flags, + module_il_path, + module_native_path, + clr_instance_get_id (), + NULL, + NULL); + + FireEtwAssemblyDCEnd_V1 ( + assembly_id, + domain_id, + binding_id, + assembly_flags, + assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +fire_domain_rundown_events_func ( + const uint64_t domain_id, + const uint32_t domain_flags, + const ep_char8_t *domain_name, + const uint32_t domain_index, + void *user_data) +{ + return FireEtwAppDomainDCEnd_V1 ( + domain_id, + domain_flags, + domain_name, + domain_index, + clr_instance_get_id (), + NULL, + NULL); +} + +static +void +eventpipe_fire_method_events ( + MonoJitInfo *ji, + MonoMethod *method, + EventPipeFireMethodEventsData *events_data) +{ + EP_ASSERT (ji != NULL); + EP_ASSERT (events_data->domain != NULL); + EP_ASSERT (events_data->method_events_func != NULL); + + uint64_t method_id = 0; + uint64_t module_id = 0; + uint64_t method_code_start = (uint64_t)ji->code_start; + uint32_t method_code_size = (uint32_t)ji->code_size; + uint32_t method_token = 0; + uint32_t method_flags = 0; + uint8_t kind = MONO_CLASS_DEF; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + + if (method) { + method_id = (uint64_t)method; + method_token = method->token; + + if (mono_jit_info_get_generic_sharing_context (ji)) + method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; + + if (method->dynamic) + method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; + + if (!ji->from_aot && !ji->from_llvm) { + method_flags |= METHOD_FLAGS_JITTED_METHOD; + if (method->wrapper_type != MONO_WRAPPER_NONE) + method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; + } + + if (method->is_generic || method->is_inflated) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + kind = m_class_get_class_kind (method->klass); + if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + } + + if (verbose) { + method_name = method->name; + method_signature = mono_signature_full_name (method->signature); + if (method->klass) + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + } + + } + + uint16_t offset_entries = 0; + uint32_t *il_offsets = NULL; + uint32_t *native_offsets = NULL; + + MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, events_data->domain) : NULL; + if (debug_info) { + offset_entries = debug_info->num_line_numbers; + if (offset_entries != 0) { + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (!events_data->buffer || needed_size > events_data->buffer_size) { + g_free (events_data->buffer); + events_data->buffer_size = (size_t)(needed_size * 1.5); + events_data->buffer = g_new (uint8_t, events_data->buffer_size); + } + + if (events_data->buffer) { + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; + + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } + } + } + + mono_debug_free_method_jit_info (debug_info); + } + + if (events_data->buffer && !il_offsets && !native_offsets) { + // No IL offset -> Native offset mapping available. Put all code on IL offset 0. + EP_ASSERT (events_data->buffer_size >= sizeof (uint32_t) * 2); + offset_entries = 1; + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; + il_offsets [0] = 0; + native_offsets [0] = (uint32_t)ji->code_size; + } + + events_data->method_events_func ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags, + (ep_char8_t *)method_namespace, + (ep_char8_t *)method_name, + (ep_char8_t *)method_signature, + offset_entries, + il_offsets, + native_offsets, + (ji->from_aot || ji->from_llvm), + verbose, + NULL); + + g_free (method_namespace); + g_free (method_signature); +} + +static +inline +bool +include_method (MonoMethod *method) +{ + if (!method) { + return false; + } else if (!m_method_is_wrapper (method)) { + return true; + } else { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; + } +} + +static +void +eventpipe_fire_method_events_func ( + MonoJitInfo *ji, + void *user_data) +{ + EventPipeFireMethodEventsData *events_data = (EventPipeFireMethodEventsData *)user_data; + EP_ASSERT (events_data != NULL); + + if (ji && !ji->is_trampoline && !ji->async) { + MonoMethod *method = jinfo_get_method (ji); + if (include_method (method)) + eventpipe_fire_method_events (ji, method, events_data); + } +} + +static +void +eventpipe_fire_assembly_events ( + MonoDomain *domain, + MonoAssembly *assembly, + ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func) +{ + EP_ASSERT (domain != NULL); + EP_ASSERT (assembly != NULL); + EP_ASSERT (assembly_events_func != NULL); + + uint64_t domain_id = (uint64_t)domain; + uint64_t module_id = (uint64_t)assembly->image; + uint64_t assembly_id = (uint64_t)assembly; + + // TODO: Extract all module IL/Native paths and pdb metadata when available. + const char *module_il_path = ""; + const char *module_il_pdb_path = ""; + const char *module_native_path = ""; + const char *module_native_pdb_path = ""; + uint8_t signature [EP_GUID_SIZE] = { 0 }; + uint32_t module_il_pdb_age = 0; + uint32_t module_native_pdb_age = 0; + + uint32_t reserved_flags = 0; + uint64_t binding_id = 0; + + // Native methods are part of JIT table and already emitted. + // TODO: FireEtwMethodDCEndVerbose_V1_or_V2 for all native methods in module as well? + + // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. + uint32_t module_flags = MODULE_FLAGS_MANIFEST_MODULE; + if (assembly->image) { + if (assembly->image->dynamic) + module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; + if (assembly->image->aot_module) + module_flags |= MODULE_FLAGS_NATIVE_MODULE; + + module_il_path = assembly->image->filename ? assembly->image->filename : ""; + } + + uint32_t assembly_flags = 0; + if (assembly->dynamic) + assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; + + if (assembly->image && assembly->image->aot_module) { + assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; + } + + char *assembly_name = mono_stringify_assembly_name (&assembly->aname); + + assembly_events_func ( + domain_id, + assembly_id, + assembly_flags, + binding_id, + (const ep_char8_t*)assembly_name, + module_id, + module_flags, + reserved_flags, + (const ep_char8_t *)module_il_path, + (const ep_char8_t *)module_native_path, + signature, + module_il_pdb_age, + (const ep_char8_t *)module_il_pdb_path, + signature, + module_native_pdb_age, + (const ep_char8_t *)module_native_pdb_path, + NULL); + + g_free (assembly_name); +} + +static +gboolean +eventpipe_execute_rundown ( + ep_rt_mono_fire_domain_rundown_events_func domain_events_func, + ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func, + ep_rt_mono_fire_method_rundown_events_func method_events_func) +{ + EP_ASSERT (domain_events_func != NULL); + EP_ASSERT (assembly_events_func != NULL); + EP_ASSERT (method_events_func != NULL); + + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + if (root_domain) { + uint64_t domain_id = (uint64_t)root_domain; + + // Emit all functions in use (JIT, AOT and Interpreter). + EventPipeFireMethodEventsData events_data; + events_data.domain = root_domain; + events_data.buffer_size = 1024 * sizeof(uint32_t); + events_data.buffer = g_new (uint8_t, events_data.buffer_size); + events_data.method_events_func = method_events_func; + + // All called JIT/AOT methods should be included in jit info table. + mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); + + // All called interpreted methods should be included in interpreter jit info table. + if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) + mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); + + // Phantom methods injected in callstacks representing runtime functions. + if (_ep_rt_mono_runtime_helper_compile_method_jitinfo && _ep_rt_mono_runtime_helper_compile_method) + eventpipe_fire_method_events (_ep_rt_mono_runtime_helper_compile_method_jitinfo, _ep_rt_mono_runtime_helper_compile_method, &events_data); + if (_ep_rt_mono_monitor_enter_method_jitinfo && _ep_rt_mono_monitor_enter_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_method_jitinfo, _ep_rt_mono_monitor_enter_method, &events_data); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo && _ep_rt_mono_monitor_enter_v4_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_v4_method_jitinfo, _ep_rt_mono_monitor_enter_v4_method, &events_data); + + g_free (events_data.buffer); + + // Iterate all assemblies in domain. + GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); + if (assemblies) { + for (int i = 0; i < assemblies->len; ++i) { + MonoAssembly *assembly = (MonoAssembly *)g_ptr_array_index (assemblies, i); + if (assembly) + eventpipe_fire_assembly_events (root_domain, assembly, assembly_events_func); + } + g_ptr_array_free (assemblies, TRUE); + } + + uint32_t domain_flags = DOMAIN_FLAGS_DEFAULT_DOMAIN | DOMAIN_FLAGS_EXECUTABLE_DOMAIN; + const char *domain_name = root_domain->friendly_name ? root_domain->friendly_name : ""; + uint32_t domain_index = 1; + + domain_events_func ( + domain_id, + domain_flags, + (const ep_char8_t *)domain_name, + domain_index, + NULL); + } + + return TRUE; +} + +inline +static +bool +in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) + return true; + + return false; +} + +inline +static +bool +in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && + (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_v4_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) + return true; + + return false; +} + +static gboolean eventpipe_walk_managed_stack_for_thread ( MonoStackFrameInfo *frame, MonoContext *ctx, EventPipeStackWalkData *stack_walk_data) { - EP_ASSERT (frame != NULL); - EP_ASSERT (stack_walk_data != NULL); + EP_ASSERT (frame != NULL); + EP_ASSERT (stack_walk_data != NULL); + + switch (frame->type) { + case FRAME_TYPE_DEBUGGER_INVOKE: + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + case FRAME_TYPE_INTERP_TO_MANAGED: + case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + case FRAME_TYPE_INTERP_ENTRY: + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_JIT_ENTRY: + // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. + // Makes it possible to detect stacks waiting on JIT compiler. + if (_ep_rt_mono_runtime_helper_compile_method && stack_walk_data->top_frame) + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_runtime_helper_compile_method), _ep_rt_mono_runtime_helper_compile_method); + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_MANAGED: + case FRAME_TYPE_INTERP: + if (frame->ji) { + stack_walk_data->async_frame |= frame->ji->async; + MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; + if (method && m_method_is_wrapper (method)) { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->safe_point_frame = true; + }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->runtime_invoke_frame = true; + } else if (_ep_rt_mono_monitor_enter_method && in_monitor_enter_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_method), _ep_rt_mono_monitor_enter_method); + } else if (_ep_rt_mono_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_v4_method), _ep_rt_mono_monitor_enter_v4_method); + } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } + } else if (method && !m_method_is_wrapper (method)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + } + } + stack_walk_data->top_frame = false; + return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; + default: + EP_UNREACHABLE ("eventpipe_walk_managed_stack_for_thread"); + return FALSE; + } +} + +static +gboolean +eventpipe_walk_managed_stack_for_thread_func ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + return eventpipe_walk_managed_stack_for_thread (frame, ctx, (EventPipeStackWalkData *)data); +} + +static +gboolean +eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + EP_ASSERT (frame != NULL); + EP_ASSERT (data != NULL); + + EventPipeSampleProfileStackWalkData *sample_data = (EventPipeSampleProfileStackWalkData *)data; + + if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { + switch (frame->type) { + case FRAME_TYPE_MANAGED: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + break; + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_JIT_ENTRY: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP: + sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP_TO_MANAGED: + case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + break; + default: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } + } + + return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); +} + +static +void +profiler_eventpipe_thread_exited ( + MonoProfiler *prof, + uintptr_t tid) +{ + void ep_rt_mono_thread_exited (void); + ep_rt_mono_thread_exited (); +} + +static bool +parse_mono_profiler_options (const ep_char8_t *option) +{ + do { + if (!*option) + return false; + + if (!strncmp (option, "alloc", 5)) { + mono_profiler_enable_allocations (); + option += 5; + } else if (!strncmp (option, "exception", 9)) { + mono_profiler_enable_clauses (); + option += 9; + /*} else if (!strncmp (option, "sample", 6)) { + mono_profiler_enable_sampling (_ep_rt_dotnet_mono_profiler_provider); + option += 6;*/ + } else { + return false; + } + + if (*option == ',') + option++; + } while (*option); + + return true; +} + +void +ep_rt_mono_component_init (void) +{ + _ep_rt_default_profiler = mono_profiler_create (NULL); + _ep_rt_dotnet_runtime_profiler_provider = mono_profiler_create (NULL); + _ep_rt_dotnet_mono_profiler_provider = mono_profiler_create (NULL); + + char *diag_env = g_getenv("MONO_DIAGNOSTICS"); + if (diag_env) { + int diag_argc = 1; + char **diag_argv = g_new (char *, 1); + if (diag_argv) { + diag_argv [0] = NULL; + if (!mono_parse_options_from (diag_env, &diag_argc, &diag_argv)) { + for (int i = 0; i < diag_argc; ++i) { + if (diag_argv [i]) { + if (strncmp (diag_argv [i], "--diagnostic-mono-profiler=", 27) == 0) { + if (!parse_mono_profiler_options (diag_argv [i] + 27)) + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable option: %s", diag_argv [i]); + } else if (strncmp (diag_argv [i], "--diagnostic-mono-profiler-callspec=", 36) == 0) { + char *errstr = NULL; + if (!mono_callspec_parse (diag_argv [i] + 36, &_ep_rt_dotnet_mono_profiler_provider_callspec, &errstr)) { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing '%s': %s", diag_argv [i], errstr); + g_free (errstr); + mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); + } else { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); + } + } else if (strncmp (diag_argv [i], "--diagnostic-ports=", 19) == 0) { + char *diag_ports_env = g_getenv("DOTNET_DiagnosticPorts"); + if (diag_ports_env) + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DIAGNOSTICS, "DOTNET_DiagnosticPorts environment variable already set, ignoring --diagnostic-ports used in MONO_DIAGNOSTICS environment variable"); + else + g_setenv ("DOTNET_DiagnosticPorts", diag_argv [i] + 19, TRUE); + g_free (diag_ports_env); + + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable, unknown option: %s", diag_argv [i]); + } + + g_free (diag_argv [i]); + diag_argv [i] = NULL; + } + } + + g_free (diag_argv); + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable"); + } + } + } + g_free (diag_env); +} + +void +ep_rt_mono_init (void) +{ + mono_native_tls_alloc (&_ep_rt_mono_thread_holder_tls_id, NULL); + + mono_100ns_ticks (); + mono_rand_open (); + _ep_rt_mono_rand_provider = mono_rand_init (NULL, 0); + + _ep_rt_mono_initialized = TRUE; + + EP_ASSERT (_ep_rt_default_profiler != NULL); + EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); + EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); + + mono_profiler_set_thread_stopped_callback (_ep_rt_default_profiler, profiler_eventpipe_thread_exited); + + MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); + if (method_signature) { + method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); + method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); + + ERROR_DECL (error); + MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); + if (is_ok (error) && runtime_helpers) { + MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); + if (method_builder) { + _ep_rt_mono_runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); + mono_mb_free (method_builder); + } + } + mono_error_cleanup (error); + mono_metadata_free_method_signature (method_signature); + + if (_ep_rt_mono_runtime_helper_compile_method) { + _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_runtime_helper_compile_method) { + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; + _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + } + } + } + + { + ERROR_DECL (error); + MonoMethodDesc *desc = NULL; + MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); + if (is_ok (error) && monitor) { + desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + if (_ep_rt_mono_monitor_enter_v4_method) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; + } + } + } + + desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + if (_ep_rt_mono_monitor_enter_method ) { + _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_method_jitinfo) { + _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); + _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; + } + } + } + } + mono_error_cleanup (error); + } +} + +void +ep_rt_mono_init_finish (void) +{ + if (mono_runtime_get_no_exec ()) + return; + + // Managed init of diagnostics classes, like registration of RuntimeEventSource (if available). + ERROR_DECL (error); + + MonoClass *runtime_event_source = mono_class_from_name_checked (mono_get_corlib (), "System.Diagnostics.Tracing", "RuntimeEventSource", error); + if (is_ok (error) && runtime_event_source) { + MonoMethod *init = mono_class_get_method_from_name_checked (runtime_event_source, "Initialize", -1, 0, error); + if (is_ok (error) && init) { + mono_runtime_try_invoke_handle (init, NULL_HANDLE, NULL, error); + } + } + + mono_error_cleanup (error); +} + +void +ep_rt_mono_fini (void) +{ + if (_ep_rt_mono_sampled_thread_callstacks) + g_array_free (_ep_rt_mono_sampled_thread_callstacks, TRUE); + + if (_ep_rt_mono_initialized) + mono_rand_close (_ep_rt_mono_rand_provider); + + g_free (_ep_rt_mono_runtime_helper_compile_method_jitinfo); + _ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; + + mono_free_method (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_method_jitinfo); + _ep_rt_mono_monitor_enter_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_v4_method_jitinfo); + _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_v4_method = NULL; + + if (_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); + } + + _ep_rt_mono_sampled_thread_callstacks = NULL; + _ep_rt_mono_rand_provider = NULL; + _ep_rt_mono_initialized = FALSE; +} + +bool +ep_rt_mono_rand_try_get_bytes ( + uint8_t *buffer, + size_t buffer_size) +{ + EP_ASSERT (_ep_rt_mono_rand_provider != NULL); + + ERROR_DECL (error); + return mono_rand_try_get_bytes (&_ep_rt_mono_rand_provider, (guchar *)buffer, (gssize)buffer_size, error); +} + +EventPipeThread * +ep_rt_mono_thread_get_or_create (void) +{ + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); + if (!thread_holder) { + thread_holder = thread_holder_alloc_func (); + mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, thread_holder); + } + return ep_thread_holder_get_thread (thread_holder); +} + +void * +ep_rt_mono_thread_attach (bool background_thread) +{ + MonoThread *thread = NULL; + + // NOTE, under netcore, only root domain exists. + if (!mono_thread_current ()) { + thread = mono_thread_internal_attach (mono_get_root_domain ()); + if (background_thread && thread) { + mono_thread_set_state (thread, ThreadState_Background); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); + } + } + + return thread; +} + +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) +{ + void *result = ep_rt_mono_thread_attach (background_thread); + if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { + // Increase sampling thread priority, accepting failures. +#ifdef HOST_WIN32 + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); +#elif _POSIX_PRIORITY_SCHEDULING + int policy; + int priority; + struct sched_param param; + int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); + if (schedparam_result == 0) { + // Attempt to switch the thread to real time scheduling. This will not + // necessarily work on all OSs; for example, most Linux systems will give + // us EPERM here unless configured to allow this. + priority = param.sched_priority; + param.sched_priority = sched_get_priority_max (SCHED_RR); + if (param.sched_priority != -1) { + schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); + if (schedparam_result != 0) { + // Fallback, attempt to increase to max priority using current policy. + param.sched_priority = sched_get_priority_max (policy); + if (param.sched_priority != -1 && param.sched_priority != priority) + pthread_setschedparam (pthread_self (), policy, ¶m); + } + } + } +#endif + } + + return result; +} + +void +ep_rt_mono_thread_detach (void) +{ + MonoThread *current_thread = mono_thread_current (); + if (current_thread) + mono_thread_internal_detach (current_thread); +} + +void +ep_rt_mono_thread_exited (void) +{ + if (_ep_rt_mono_initialized) { + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); + if (thread_holder) + thread_holder_free_func (thread_holder); + mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, NULL); + } +} + +#ifdef HOST_WIN32 +int64_t +ep_rt_mono_perf_counter_query (void) +{ + LARGE_INTEGER value; + if (QueryPerformanceCounter (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} + +int64_t +ep_rt_mono_perf_frequency_query (void) +{ + LARGE_INTEGER value; + if (QueryPerformanceFrequency (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} + +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +{ + SYSTEMTIME value; + GetSystemTime (&value); + + EP_ASSERT (system_time != NULL); + ep_system_time_set ( + system_time, + value.wYear, + value.wMonth, + value.wDayOfWeek, + value.wDay, + value.wHour, + value.wMinute, + value.wSecond, + value.wMilliseconds); +} + +int64_t +ep_rt_mono_system_timestamp_get (void) +{ + FILETIME value; + GetSystemTimeAsFileTime (&value); + return (int64_t)((((uint64_t)value.dwHighDateTime) << 32) | (uint64_t)value.dwLowDateTime); +} +#else +#include +#include +#include +#include + +#if HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H + +#if HAVE_MACH_ABSOLUTE_TIME +#include +static mono_lazy_init_t _ep_rt_mono_time_base_info_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +static mach_timebase_info_data_t _ep_rt_mono_time_base_info = {0}; +#endif + +#ifdef HAVE_LOCALTIME_R +#define HAVE_GMTIME_R 1 +#endif + +static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; +static const int64_t SECS_TO_100NS = 10000000; +static const int64_t SECS_TO_NS = 1000000000; +static const int64_t MSECS_TO_MIS = 1000; + +/* clock_gettime () is found by configure on Apple builds, but its only present from ios 10, macos 10.12, tvos 10 and watchos 3 */ +#if defined (HAVE_CLOCK_MONOTONIC) && (defined(TARGET_IOS) || defined(TARGET_OSX) || defined(TARGET_WATCHOS) || defined(TARGET_TVOS)) +#undef HAVE_CLOCK_MONOTONIC +#endif + +#ifndef HAVE_CLOCK_MONOTONIC +static const int64_t MISECS_TO_NS = 1000; +#endif + +static +void +time_base_info_lazy_init (void); + +static +int64_t +system_time_to_int64 ( + time_t sec, + long nsec); + +#if HAVE_MACH_ABSOLUTE_TIME +static +void +time_base_info_lazy_init (void) +{ + kern_return_t result = mach_timebase_info (&_ep_rt_mono_time_base_info); + if (result != KERN_SUCCESS) + memset (&_ep_rt_mono_time_base_info, 0, sizeof (_ep_rt_mono_time_base_info)); +} +#endif + +int64_t +ep_rt_mono_perf_counter_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + return (int64_t)mach_absolute_time (); +#elif HAVE_CLOCK_MONOTONIC + struct timespec ts; + int result = clock_gettime (CLOCK_MONOTONIC, &ts); + if (result == 0) + return ((int64_t)(ts.tv_sec) * (int64_t)(SECS_TO_NS)) + (int64_t)(ts.tv_nsec); +#else + #error "ep_rt_mono_perf_counter_get requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; +} + +int64_t +ep_rt_mono_perf_frequency_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + // (numer / denom) gives you the nanoseconds per tick, so the below code + // computes the number of ticks per second. We explicitly do the multiplication + // first in order to help minimize the error that is produced by integer division. + mono_lazy_initialize (&_ep_rt_mono_time_base_info_init, time_base_info_lazy_init); + if (_ep_rt_mono_time_base_info.denom == 0 || _ep_rt_mono_time_base_info.numer == 0) + return 0; + return ((int64_t)(SECS_TO_NS) * (int64_t)(_ep_rt_mono_time_base_info.denom)) / (int64_t)(_ep_rt_mono_time_base_info.numer); +#elif HAVE_CLOCK_MONOTONIC + // clock_gettime () returns a result in terms of nanoseconds rather than a count. This + // means that we need to either always scale the result by the actual resolution (to + // get a count) or we need to say the resolution is in terms of nanoseconds. We prefer + // the latter since it allows the highest throughput and should minimize error propagated + // to the user. + return (int64_t)(SECS_TO_NS); +#else + #error "ep_rt_mono_perf_frequency_query requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; +} + +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +{ + time_t tt; +#if HAVE_GMTIME_R + struct tm ut; +#endif /* HAVE_GMTIME_R */ + struct tm *ut_ptr; + struct timeval time_val; + int timeofday_retval; + + EP_ASSERT (system_time != NULL); + + tt = time (NULL); + + /* We can't get millisecond resolution from time (), so we get it from gettimeofday () */ + timeofday_retval = gettimeofday (&time_val, NULL); + +#if HAVE_GMTIME_R + ut_ptr = &ut; + if (gmtime_r (&tt, ut_ptr) == NULL) +#else /* HAVE_GMTIME_R */ + if ((ut_ptr = gmtime (&tt)) == NULL) +#endif /* HAVE_GMTIME_R */ + EP_UNREACHABLE (); + + uint16_t milliseconds = 0; + if (timeofday_retval != -1) { + int old_seconds; + int new_seconds; + + milliseconds = time_val.tv_usec / MSECS_TO_MIS; + + old_seconds = ut_ptr->tm_sec; + new_seconds = time_val.tv_sec % 60; + + /* just in case we reached the next second in the interval between time () and gettimeofday () */ + if (old_seconds != new_seconds) + milliseconds = 999; + } + + ep_system_time_set ( + system_time, + 1900 + ut_ptr->tm_year, + ut_ptr->tm_mon + 1, + ut_ptr->tm_wday, + ut_ptr->tm_mday, + ut_ptr->tm_hour, + ut_ptr->tm_min, + ut_ptr->tm_sec, + milliseconds); +} + +static +inline +int64_t +system_time_to_int64 ( + time_t sec, + long nsec) +{ + return ((int64_t)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + (nsec / 100); +} + +int64_t +ep_rt_mono_system_timestamp_get (void) +{ +#if HAVE_CLOCK_MONOTONIC + struct timespec time; + if (clock_gettime (CLOCK_REALTIME, &time) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_nsec); +#else + struct timeval time; + if (gettimeofday (&time, NULL) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_usec * MISECS_TO_NS); +#endif + else + return system_time_to_int64 (0, 0); +} +#endif + +#ifndef HOST_WIN32 +#if defined(__APPLE__) +#if defined (TARGET_OSX) +G_BEGIN_DECLS +gchar ***_NSGetEnviron(void); +G_END_DECLS +#define environ (*_NSGetEnviron()) +#else +static char *_ep_rt_mono_environ[1] = { NULL }; +#define environ _ep_rt_mono_environ +#endif /* defined (TARGET_OSX) */ +#else +G_BEGIN_DECLS +extern char **environ; +G_END_DECLS +#endif /* defined (__APPLE__) */ +#endif /* !defined (HOST_WIN32) */ + +void +ep_rt_mono_os_environment_get_utf16 (ep_rt_env_array_utf16_t *env_array) +{ + EP_ASSERT (env_array != NULL); +#ifdef HOST_WIN32 + LPWSTR envs = GetEnvironmentStringsW (); + if (envs) { + LPWSTR next = envs; + while (*next) { + ep_rt_env_array_utf16_append (env_array, ep_rt_utf16_string_dup (next)); + next += ep_rt_utf16_string_len (next) + 1; + } + FreeEnvironmentStringsW (envs); + } +#else + gchar **next = NULL; + for (next = environ; *next != NULL; ++next) + ep_rt_env_array_utf16_append (env_array, ep_rt_utf8_to_utf16_string (*next, -1)); +#endif +} + +void +ep_rt_mono_init_providers_and_events (void) +{ + extern void InitProvidersAndEvents (void); + InitProvidersAndEvents (); +} + +void +ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config) +{ + if (!ep_rt_utf8_string_compare (ep_config_get_rundown_provider_name_utf8 (), ep_provider_config_get_provider_name (provider_config))) { + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = ep_provider_config_get_logging_level (provider_config); + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = ep_provider_config_get_keywords (provider_config); + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = true; + } +} + +bool +ep_rt_mono_providers_validate_all_disabled (void) +{ + return (!MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled && + !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled && + !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled && + !MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled); +} + +void +ep_rt_mono_fini_providers_and_events (void) +{ + // dotnet/runtime: issue 12775: EventPipe shutdown race conditions + // Deallocating providers/events here might cause AV if a WriteEvent + // was to occur. Thus, we are not doing this cleanup. +} + +bool +ep_rt_mono_walk_managed_stack_for_thread ( + ep_rt_thread_handle_t thread, + EventPipeStackContents *stack_contents) +{ + EP_ASSERT (thread != NULL && stack_contents != NULL); + + EventPipeStackWalkData stack_walk_data; + stack_walk_data.stack_contents = stack_contents; + stack_walk_data.top_frame = true; + stack_walk_data.async_frame = false; + stack_walk_data.safe_point_frame = false; + stack_walk_data.runtime_invoke_frame = false; + + if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); + else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); + + return true; +} + +bool +ep_rt_mono_method_get_simple_assembly_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len) +{ + EP_ASSERT (method != NULL); + EP_ASSERT (name != NULL); + + MonoClass *method_class = mono_method_get_class (method); + MonoImage *method_image = method_class ? mono_class_get_image (method_class) : NULL; + const ep_char8_t *assembly_name = method_image ? mono_image_get_name (method_image) : NULL; + + if (!assembly_name) + return false; + + g_strlcpy (name, assembly_name, name_len); + return true; +} + +bool +ep_rt_mono_method_get_full_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len) +{ + EP_ASSERT (method != NULL); + EP_ASSERT (name != NULL); + + char *full_method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); + if (!full_method_name) + return false; + + g_strlcpy (name, full_method_name, name_len); + + g_free (full_method_name); + return true; +} + +bool +ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( + ep_rt_thread_handle_t sampling_thread, + EventPipeEvent *sampling_event) +{ + // Follows CoreClr implementation of sample profiler. Generic invasive/expensive way to do CPU sample profiling relying on STW and stackwalks. + // TODO: Investigate alternatives on platforms supporting Signals/SuspendThread (see Mono profiler) or CPU PMU's (see ETW/perf_event_open). + + // Sample profiler only runs on one thread, no need to synchorinize. + if (!_ep_rt_mono_sampled_thread_callstacks) + _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileStackWalkData), _ep_rt_mono_max_sampled_thread_count); + + // Make sure there is room based on previous max number of sampled threads. + // NOTE, there is a chance there are more threads than max, if that's the case we will + // miss those threads in this sample, but will be included in next when max has been adjusted. + g_array_set_size (_ep_rt_mono_sampled_thread_callstacks, _ep_rt_mono_max_sampled_thread_count); + + uint32_t filtered_thread_count = 0; + uint32_t sampled_thread_count = 0; + + mono_stop_world (MONO_THREAD_INFO_FLAGS_NO_GC); + + gboolean async_context = mono_thread_info_is_async_context (); + mono_thread_info_set_is_async_context (TRUE); + + // Record all info needed in sample events while runtime is suspended, must be async safe. + FOREACH_THREAD_SAFE_EXCLUDE (thread_info, MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE) { + if (!mono_thread_info_is_running (thread_info)) { + MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); + if (thread_state->valid) { + if (sampled_thread_count < _ep_rt_mono_max_sampled_thread_count) { + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, sampled_thread_count); + data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); + data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; + data->stack_walk_data.stack_contents = &data->stack_contents; + data->stack_walk_data.top_frame = true; + data->stack_walk_data.async_frame = false; + data->stack_walk_data.safe_point_frame = false; + data->stack_walk_data.runtime_invoke_frame = false; + ep_stack_contents_reset (&data->stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { + // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame + // as second, re-classify current callstack to be executing managed code. + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } + + sampled_thread_count++; + } + } + } + filtered_thread_count++; + } FOREACH_THREAD_SAFE_END + + mono_thread_info_set_is_async_context (async_context); + mono_restart_world (MONO_THREAD_INFO_FLAGS_NO_GC); + + // Fire sample event for threads. Must be done after runtime is resumed since it's not async safe. + // Since we can't keep thread info around after runtime as been suspended, use an empty + // adapter instance and only set recorded tid as parameter inside adapter. + THREAD_INFO_TYPE adapter = { { 0 } }; + for (uint32_t i = 0; i < sampled_thread_count; ++i) { + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, i); + if (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length(&data->stack_contents) > 0) { + // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. + // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event + // for this specific stackwalk or we could cleanup stack_frames before writing profile event. + if (data->stack_walk_data.async_frame) { + for (int i = 0; i < data->stack_contents.next_available_frame; ++i) + mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [i], TRUE, FALSE); + } + mono_thread_info_set_tid (&adapter, ep_rt_uint64_t_to_thread_id_t (data->thread_id)); + ep_write_sample_profile_event (sampling_thread, sampling_event, &adapter, &data->stack_contents, (uint8_t *)&data->payload_data, sizeof (data->payload_data)); + } + } + + // Current thread count will be our next maximum sampled threads. + _ep_rt_mono_max_sampled_thread_count = filtered_thread_count; + + return true; +} + +void +ep_rt_mono_execute_rundown (ep_rt_execution_checkpoint_array_t *execution_checkpoints) +{ + ep_char8_t runtime_module_path [256]; + const uint8_t object_guid [EP_GUID_SIZE] = { 0 }; + const uint16_t runtime_product_qfe_version = 0; + const uint32_t startup_flags = 0; + const uint8_t startup_mode = 0; + const ep_char8_t *command_line = ""; + + if (!g_module_address ((void *)mono_init, runtime_module_path, sizeof (runtime_module_path), NULL, NULL, 0, NULL)) + runtime_module_path [0] = '\0'; + + FireEtwRuntimeInformationDCStart ( + clr_instance_get_id (), + RUNTIME_SKU_CORECLR, + RuntimeProductMajorVersion, + RuntimeProductMinorVersion, + RuntimeProductPatchVersion, + runtime_product_qfe_version, + RuntimeFileMajorVersion, + RuntimeFileMajorVersion, + RuntimeFileBuildVersion, + RuntimeFileRevisionVersion, + startup_mode, + startup_flags, + command_line, + object_guid, + runtime_module_path, + NULL, + NULL); + + if (execution_checkpoints) { + ep_rt_execution_checkpoint_array_iterator_t execution_checkpoints_iterator = ep_rt_execution_checkpoint_array_iterator_begin (execution_checkpoints); + while (!ep_rt_execution_checkpoint_array_iterator_end (execution_checkpoints, &execution_checkpoints_iterator)) { + EventPipeExecutionCheckpoint *checkpoint = ep_rt_execution_checkpoint_array_iterator_value (&execution_checkpoints_iterator); + FireEtwExecutionCheckpointDCEnd ( + clr_instance_get_id (), + checkpoint->name, + checkpoint->timestamp, + NULL, + NULL); + ep_rt_execution_checkpoint_array_iterator_next (&execution_checkpoints_iterator); + } + } + + FireEtwDCEndInit_V1 ( + clr_instance_get_id (), + NULL, + NULL); + + eventpipe_execute_rundown ( + fire_domain_rundown_events_func, + fire_assembly_rundown_events_func, + fire_method_rundown_events_func); + + FireEtwDCEndComplete_V1 ( + clr_instance_get_id (), + NULL, + NULL); +} + +bool +ep_rt_mono_write_event_ee_startup_start (void) +{ + return FireEtwEEStartupStart_V1 ( + clr_instance_get_id (), + NULL, + NULL); +} + +bool +ep_rt_mono_write_event_jit_start (MonoMethod *method) +{ + if (!EventEnabledMethodJittingStarted_V1 ()) + return true; + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + if (method) { + uint64_t method_id = 0; + uint64_t module_id = 0; + uint32_t code_size = 0; + uint32_t method_token = 0; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + + //TODO: SendMethodDetailsEvent + + method_id = (uint64_t)method; + + if (!method->dynamic) + method_token = method->token; + + if (!mono_method_has_no_body (method)) { + ERROR_DECL (error); + MonoMethodHeader *header = mono_method_get_header_internal (method, error); + if (header) + code_size = header->code_size; + } + + method_name = method->name; + method_signature = mono_signature_full_name (method->signature); + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + } + + FireEtwMethodJittingStarted_V1 ( + method_id, + module_id, + method_token, + code_size, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + g_free (method_namespace); + g_free (method_signature); + } + + return true; +} + +bool +ep_rt_mono_write_event_method_il_to_native_map ( + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMethodILToNativeMap ()) + return true; + + if (method) { + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + uint64_t method_id = (uint64_t)method; + uint32_t fixed_buffer [64]; + uint8_t *buffer = NULL; + + uint16_t offset_entries = 0; + uint32_t *il_offsets = NULL; + uint32_t *native_offsets = NULL; + + MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; + if (debug_info) { + if (offset_entries != 0) { + offset_entries = debug_info->num_line_numbers; + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (needed_size > sizeof (fixed_buffer)) { + buffer = g_new (uint8_t, needed_size); + il_offsets = (uint32_t*)buffer; + } else { + il_offsets = fixed_buffer; + } + if (il_offsets) { + native_offsets = il_offsets + offset_entries; + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } + } + } + + mono_debug_free_method_jit_info (debug_info); + } + + if (!il_offsets && !native_offsets) { + // No IL offset -> Native offset mapping available. Put all code on IL offset 0. + EP_ASSERT (sizeof (fixed_buffer) >= sizeof (uint32_t) * 2); + offset_entries = 1; + il_offsets = fixed_buffer; + native_offsets = il_offsets + offset_entries; + il_offsets [0] = 0; + native_offsets [0] = ji ? (uint32_t)ji->code_size : 0; + } + + FireEtwMethodILToNativeMap ( + method_id, + 0, + 0, + offset_entries, + il_offsets, + native_offsets, + clr_instance_get_id (), + NULL, + NULL); + + g_free (buffer); + } + + return true; +} + +bool +ep_rt_mono_write_event_method_load ( + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMethodLoad_V1 () && !EventEnabledMethodLoadVerbose_V1()) + return true; + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + if (method) { + uint64_t method_id = 0; + uint64_t module_id = 0; + uint64_t method_code_start = ji ? (uint64_t)ji->code_start : 0; + uint32_t method_code_size = ji ? (uint32_t)ji->code_size : 0; + uint32_t method_token = 0; + uint32_t method_flags = 0; + uint8_t kind = MONO_CLASS_DEF; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + + method_id = (uint64_t)method; + + if (!method->dynamic) + method_token = method->token; + + if (ji && mono_jit_info_get_generic_sharing_context (ji)) { + method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; + verbose = true; + } + + if (method->dynamic) { + method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; + verbose = true; + } + + if (ji && !ji->from_aot && !ji->from_llvm) { + method_flags |= METHOD_FLAGS_JITTED_METHOD; + if (method->wrapper_type != MONO_WRAPPER_NONE) + method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; + } + + if (method->is_generic || method->is_inflated) { + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + verbose = true; + } + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + kind = m_class_get_class_kind (method->klass); + if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + } + + //TODO: SendMethodDetailsEvent + + if (verbose) { + method_name = method->name; + method_signature = mono_signature_full_name (method->signature); + + if (method->klass) + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + + FireEtwMethodLoadVerbose_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + if (ji && (ji->from_aot || ji->from_llvm)) + FireEtwMethodLoadVerbose_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + } else { + FireEtwMethodLoad_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + clr_instance_get_id (), + NULL, + NULL); + + if (ji && (ji->from_aot || ji->from_llvm)) + FireEtwMethodLoad_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + clr_instance_get_id (), + NULL, + NULL); + } + + g_free (method_namespace); + g_free (method_signature); + } + + return true; +} + +static +bool +get_module_event_data ( + MonoImage *image, + ModuleEventData *module_data) +{ + if (image && module_data) { + memset (module_data->signature, 0, EP_GUID_SIZE); + + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + module_data->domain_id = (uint64_t)root_domain; + module_data->module_id = (uint64_t)image; + module_data->assembly_id = (uint64_t)image->assembly; + + // TODO: Extract all module IL/Native paths and pdb metadata when available. + module_data->module_il_path = ""; + module_data->module_il_pdb_path = ""; + module_data->module_native_path = ""; + module_data->module_native_pdb_path = ""; + + module_data->module_il_pdb_age = 0; + module_data->module_native_pdb_age = 0; + + module_data->reserved_flags = 0; + + // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. + module_data->module_flags = MODULE_FLAGS_MANIFEST_MODULE; + if (image->dynamic) + module_data->module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; + if (image->aot_module) + module_data->module_flags |= MODULE_FLAGS_NATIVE_MODULE; + + module_data->module_il_path = image->filename ? image->filename : ""; + } + + return true; +} + +bool +ep_rt_mono_write_event_module_load (MonoImage *image) +{ + if (!EventEnabledModuleLoad_V2 () && !EventEnabledDomainModuleLoad_V1()) + return true; - switch (frame->type) { - case FRAME_TYPE_DEBUGGER_INVOKE: - case FRAME_TYPE_MANAGED_TO_NATIVE: - case FRAME_TYPE_TRAMPOLINE: - case FRAME_TYPE_INTERP_TO_MANAGED: - case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: - case FRAME_TYPE_INTERP_ENTRY: - stack_walk_data->top_frame = false; - return FALSE; - case FRAME_TYPE_JIT_ENTRY: - // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. - // Makes it possible to detect stacks waiting on JIT compiler. - if (_ep_rt_mono_runtime_helper_compile_method && stack_walk_data->top_frame) - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_runtime_helper_compile_method), _ep_rt_mono_runtime_helper_compile_method); - stack_walk_data->top_frame = false; - return FALSE; - case FRAME_TYPE_MANAGED: - case FRAME_TYPE_INTERP: - if (frame->ji) { - stack_walk_data->async_frame |= frame->ji->async; - MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; - if (method && m_method_is_wrapper (method)) { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); - if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { - stack_walk_data->safe_point_frame = true; - }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { - stack_walk_data->runtime_invoke_frame = true; - } else if (_ep_rt_mono_monitor_enter_method && in_monitor_enter_frame (wrapper)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_method), _ep_rt_mono_monitor_enter_method); - } else if (_ep_rt_mono_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_v4_method), _ep_rt_mono_monitor_enter_v4_method); - } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } - } else if (method && !m_method_is_wrapper (method)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); - } + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) { + FireEtwModuleLoad_V2 ( + module_data.module_id, + module_data.assembly_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + module_data.signature, + module_data.module_il_pdb_age, + module_data.module_il_pdb_path, + module_data.signature, + module_data.module_native_pdb_age, + module_data.module_native_pdb_path, + NULL, + NULL); + + FireEtwDomainModuleLoad_V1 ( + module_data.module_id, + module_data.assembly_id, + module_data.domain_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + NULL, + NULL); } - stack_walk_data->top_frame = false; - return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; - default: - EP_UNREACHABLE ("eventpipe_walk_managed_stack_for_thread"); - return FALSE; } + + return true; +} + +bool +ep_rt_mono_write_event_module_unload (MonoImage *image) +{ + if (!EventEnabledModuleUnload_V2()) + return true; + + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) { + FireEtwModuleUnload_V2 ( + module_data.module_id, + module_data.assembly_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + module_data.signature, + module_data.module_il_pdb_age, + module_data.module_il_pdb_path, + module_data.signature, + module_data.module_native_pdb_age, + module_data.module_native_pdb_path, + NULL, + NULL); + } + } + + return true; +} + +static +bool +get_assembly_event_data ( + MonoAssembly *assembly, + AssemblyEventData *assembly_data) +{ + if (assembly && assembly_data) { + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + assembly_data->domain_id = (uint64_t)root_domain; + assembly_data->assembly_id = (uint64_t)assembly; + assembly_data->binding_id = 0; + + assembly_data->assembly_flags = 0; + if (assembly->dynamic) + assembly_data->assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; + + if (assembly->image && assembly->image->aot_module) + assembly_data->assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; + + assembly_data->assembly_name = mono_stringify_assembly_name (&assembly->aname); + } + + return true; +} + +bool +ep_rt_mono_write_event_assembly_load (MonoAssembly *assembly) +{ + if (!EventEnabledAssemblyLoad_V1 ()) + return true; + + if (assembly) { + AssemblyEventData assembly_data; + if (get_assembly_event_data (assembly, &assembly_data)) { + FireEtwAssemblyLoad_V1 ( + assembly_data.assembly_id, + assembly_data.domain_id, + assembly_data.binding_id, + assembly_data.assembly_flags, + assembly_data.assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + g_free (assembly_data.assembly_name); + } + } + + return true; +} + +bool +ep_rt_mono_write_event_assembly_unload (MonoAssembly *assembly) +{ + if (!EventEnabledAssemblyUnload_V1 ()) + return true; + + if (assembly) { + AssemblyEventData assembly_data; + if (get_assembly_event_data (assembly, &assembly_data)) { + FireEtwAssemblyUnload_V1 ( + assembly_data.assembly_id, + assembly_data.domain_id, + assembly_data.binding_id, + assembly_data.assembly_flags, + assembly_data.assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + g_free (assembly_data.assembly_name); + } + } + + return true; +} + +bool +ep_rt_mono_write_event_thread_created (ep_rt_thread_id_t tid) +{ + if (!EventEnabledThreadCreated ()) + return true; + + uint64_t managed_thread = 0; + uint64_t native_thread_id = ep_rt_thread_id_t_to_uint64_t (tid); + uint64_t managed_thread_id = 0; + uint32_t flags = 0; + + MonoThread *thread = mono_thread_current (); + if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) { + managed_thread_id = (uint64_t)mono_thread_get_managed_id (thread); + managed_thread = (uint64_t)thread; + + switch (mono_thread_info_get_flags (thread->thread_info)) { + case MONO_THREAD_INFO_FLAGS_NO_GC: + case MONO_THREAD_INFO_FLAGS_NO_SAMPLE: + flags |= THREAD_FLAGS_GC_SPECIAL; + } + + if (mono_gc_is_finalizer_thread (thread)) + flags |= THREAD_FLAGS_FINALIZER; + + if (thread->threadpool_thread) + flags |= THREAD_FLAGS_THREADPOOL_WORKER; + } + + FireEtwThreadCreated ( + managed_thread, + (uint64_t)mono_get_root_domain (), + flags, + managed_thread_id, + native_thread_id, + clr_instance_get_id (), + NULL, + NULL); + + return true; } -static -gboolean -eventpipe_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) +bool +ep_rt_mono_write_event_thread_terminated (ep_rt_thread_id_t tid) { - return eventpipe_walk_managed_stack_for_thread (frame, ctx, (EventPipeStackWalkData *)data); + if (!EventEnabledThreadTerminated ()) + return true; + + uint64_t managed_thread = 0; + MonoThread *thread = mono_thread_current (); + if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) + managed_thread = (uint64_t)thread; + + FireEtwThreadTerminated ( + managed_thread, + (uint64_t)mono_get_root_domain (), + clr_instance_get_id (), + NULL, + NULL); + + return true; } static -gboolean -eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) +uint32_t +get_type_start_id (MonoType *type) { - EP_ASSERT (frame != NULL); - EP_ASSERT (data != NULL); + uint32_t start_id = (uint32_t)(uintptr_t)type; - EventPipeSampleProfileStackWalkData *sample_data = (EventPipeSampleProfileStackWalkData *)data; + start_id = (((start_id * 215497) >> 16) ^ ((start_id * 1823231) + start_id)); - if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { - switch (frame->type) { - case FRAME_TYPE_MANAGED: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - break; - case FRAME_TYPE_MANAGED_TO_NATIVE: - case FRAME_TYPE_TRAMPOLINE: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_JIT_ENTRY: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_INTERP: - sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_INTERP_TO_MANAGED: - case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: - break; - default: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - } - } + // Mix in highest bits on 64-bit systems only + if (sizeof (type) > 4) + start_id = start_id ^ (((uint64_t)type >> 31) >> 1); - return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); + return start_id; } -static -void -profiler_eventpipe_thread_exited ( - MonoProfiler *prof, - uintptr_t tid) +bool +ep_rt_mono_write_event_type_load_start (MonoType *type) { - void ep_rt_mono_thread_exited (void); - ep_rt_mono_thread_exited (); + if (!EventEnabledTypeLoadStart ()) + return true; + + FireEtwTypeLoadStart ( + get_type_start_id (type), + clr_instance_get_id (), + NULL, + NULL); + + return true; } -void -ep_rt_mono_init (void) +bool +ep_rt_mono_write_event_type_load_stop (MonoType *type) { - mono_native_tls_alloc (&_ep_rt_mono_thread_holder_tls_id, NULL); + if (!EventEnabledTypeLoadStop ()) + return true; - mono_100ns_ticks (); - mono_rand_open (); - _ep_rt_mono_rand_provider = mono_rand_init (NULL, 0); + char *type_name = NULL; + if (type) + type_name = mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); - _ep_rt_mono_initialized = TRUE; + FireEtwTypeLoadStop ( + get_type_start_id (type), + clr_instance_get_id (), + 6 /* CLASS_LOADED */, + (uint64_t)type, + type_name, + NULL, + NULL); - _ep_rt_mono_profiler = mono_profiler_create (NULL); - mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_eventpipe_thread_exited); + g_free (type_name); - MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); - if (method_signature) { - method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); - method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); + return true; +} + +static +gboolean +get_exception_ip_func ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + *(uintptr_t *)data = (uintptr_t)MONO_CONTEXT_GET_IP (ctx); + return TRUE; +} + +bool +ep_rt_mono_write_event_exception_thrown (MonoObject *obj) +{ + if (!EventEnabledExceptionThrown_V1 ()) + return true; + if (obj) { ERROR_DECL (error); - MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); - if (is_ok (error) && runtime_helpers) { - MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); - if (method_builder) { - _ep_rt_mono_runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); - mono_mb_free (method_builder); - } - } - mono_error_cleanup (error); - mono_metadata_free_method_signature (method_signature); + char *type_name = NULL; + char *exception_message = NULL; + uint16_t flags = 0; + uint32_t hresult = 0; + uintptr_t ip = 0; - _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_runtime_helper_compile_method) { - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; - _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + if (mono_object_isinst_checked ((MonoObject *) obj, mono_get_exception_class (), error)) { + MonoException *exception = (MonoException *)obj; + flags |= EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT; + if (exception->inner_ex) + flags |= EXCEPTION_THROWN_FLAGS_HAS_INNER; + exception_message = ep_rt_utf16_to_utf8_string (mono_string_chars_internal (exception->message), mono_string_length_internal (exception->message)); + hresult = exception->hresult; } - } - { - ERROR_DECL (error); - MonoMethodDesc *desc = NULL; - MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); - if (is_ok (error) && monitor) { - desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); - if (desc) { - _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); - mono_method_desc_free (desc); + if (mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_SIGNAL_SAFE, (void *)&ip); - _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; - } - } + type_name = mono_type_get_name_full (m_class_get_byval_arg (mono_object_class (obj)), MONO_TYPE_NAME_FORMAT_IL); - desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); - if (desc) { - _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); - mono_method_desc_free (desc); + FireEtwExceptionThrown_V1 ( + type_name, + exception_message, + (void *)&ip, + hresult, + flags, + clr_instance_get_id (), + NULL, + NULL); - _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_method_jitinfo) { - _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); - _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; - } - } + if (!mono_component_profiler_clauses_enabled ()) { + FireEtwExceptionThrownStop ( + NULL, + NULL); } - mono_error_cleanup (error); - } -} - -void -ep_rt_mono_init_finish (void) -{ - if (mono_runtime_get_no_exec ()) - return; - // Managed init of diagnostics classes, like registration of RuntimeEventSource (if available). - ERROR_DECL (error); + g_free (exception_message); + g_free (type_name); - MonoClass *runtime_event_source = mono_class_from_name_checked (mono_get_corlib (), "System.Diagnostics.Tracing", "RuntimeEventSource", error); - if (is_ok (error) && runtime_event_source) { - MonoMethod *init = mono_class_get_method_from_name_checked (runtime_event_source, "Initialize", -1, 0, error); - if (is_ok (error) && init) { - mono_runtime_try_invoke_handle (init, NULL_HANDLE, NULL, error); - } + mono_error_cleanup (error); } - mono_error_cleanup (error); + return true; } -void -ep_rt_mono_fini (void) +bool +ep_rt_mono_write_event_exception_clause ( + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *obj) { - if (_ep_rt_mono_sampled_thread_callstacks) - g_array_free (_ep_rt_mono_sampled_thread_callstacks, TRUE); - - if (_ep_rt_mono_initialized) - mono_rand_close (_ep_rt_mono_rand_provider); + if (!mono_component_profiler_clauses_enabled ()) + return true; - g_free (_ep_rt_mono_runtime_helper_compile_method_jitinfo); - _ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; + if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE) && (!EventEnabledExceptionCatchStart() || !EventEnabledExceptionCatchStop())) + return true; - mono_free_method (_ep_rt_mono_runtime_helper_compile_method); - _ep_rt_mono_runtime_helper_compile_method = NULL; + if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER && (!EventEnabledExceptionFilterStart() || !EventEnabledExceptionFilterStop())) + return true; - g_free (_ep_rt_mono_monitor_enter_method_jitinfo); - _ep_rt_mono_monitor_enter_method_jitinfo = NULL; - _ep_rt_mono_monitor_enter_method = NULL; + if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY && (!EventEnabledExceptionFinallyStart() || !EventEnabledExceptionFinallyStop())) + return true; - g_free (_ep_rt_mono_monitor_enter_v4_method_jitinfo); - _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; - _ep_rt_mono_monitor_enter_v4_method = NULL; + uintptr_t ip = 0; //TODO: Have profiler pass along IP of handler block. + uint64_t method_id = (uint64_t)method; + char *method_name = NULL; - _ep_rt_mono_sampled_thread_callstacks = NULL; - _ep_rt_mono_rand_provider = NULL; - _ep_rt_mono_initialized = FALSE; -} + method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); -bool -ep_rt_mono_rand_try_get_bytes ( - uint8_t *buffer, - size_t buffer_size) -{ - EP_ASSERT (_ep_rt_mono_rand_provider != NULL); + if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE)) { + FireEtwExceptionCatchStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); - ERROR_DECL (error); - return mono_rand_try_get_bytes (&_ep_rt_mono_rand_provider, (guchar *)buffer, (gssize)buffer_size, error); -} + FireEtwExceptionCatchStop ( + NULL, + NULL); -EventPipeThread * -ep_rt_mono_thread_get_or_create (void) -{ - EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); - if (!thread_holder) { - thread_holder = thread_holder_alloc_func (); - mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, thread_holder); + FireEtwExceptionThrownStop ( + NULL, + NULL); } - return ep_thread_holder_get_thread (thread_holder); -} -void * -ep_rt_mono_thread_attach (bool background_thread) -{ - MonoThread *thread = NULL; + if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER) { + FireEtwExceptionFilterStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); - // NOTE, under netcore, only root domain exists. - if (!mono_thread_current ()) { - thread = mono_thread_internal_attach (mono_get_root_domain ()); - if (background_thread && thread) { - mono_thread_set_state (thread, ThreadState_Background); - mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); - } + FireEtwExceptionFilterStop ( + NULL, + NULL); } - return thread; -} + if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY) { + FireEtwExceptionFinallyStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); -void * -ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) -{ - void *result = ep_rt_mono_thread_attach (background_thread); - if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { - // Increase sampling thread priority, accepting failures. -#ifdef HOST_WIN32 - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); -#elif _POSIX_PRIORITY_SCHEDULING - int policy; - int priority; - struct sched_param param; - int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); - if (schedparam_result == 0) { - // Attempt to switch the thread to real time scheduling. This will not - // necessarily work on all OSs; for example, most Linux systems will give - // us EPERM here unless configured to allow this. - priority = param.sched_priority; - param.sched_priority = sched_get_priority_max (SCHED_RR); - if (param.sched_priority != -1) { - schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); - if (schedparam_result != 0) { - // Fallback, attempt to increase to max priority using current policy. - param.sched_priority = sched_get_priority_max (policy); - if (param.sched_priority != -1 && param.sched_priority != priority) - pthread_setschedparam (pthread_self (), policy, ¶m); - } - } - } -#endif + FireEtwExceptionFinallyStop ( + NULL, + NULL); } - return result; + g_free (method_name); + return true; } -void -ep_rt_mono_thread_detach (void) +bool +ep_rt_mono_write_event_monitor_contention_start (MonoObject *obj) { - MonoThread *current_thread = mono_thread_current (); - if (current_thread) - mono_thread_internal_detach (current_thread); -} + if (!EventEnabledContentionStart_V1 ()) + return true; -void -ep_rt_mono_thread_exited (void) -{ - if (_ep_rt_mono_initialized) { - EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); - if (thread_holder) - thread_holder_free_func (thread_holder); - mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, NULL); - } -} + FireEtwContentionStart_V1 ( + 0 /* ManagedContention */, + clr_instance_get_id (), + NULL, + NULL); -#ifdef HOST_WIN32 -int64_t -ep_rt_mono_perf_counter_query (void) -{ - LARGE_INTEGER value; - if (QueryPerformanceCounter (&value)) - return (int64_t)value.QuadPart; - else - return 0; + return true; } -int64_t -ep_rt_mono_perf_frequency_query (void) +bool +ep_rt_mono_write_event_monitor_contention_stop (MonoObject *obj) { - LARGE_INTEGER value; - if (QueryPerformanceFrequency (&value)) - return (int64_t)value.QuadPart; - else - return 0; -} + if (!EventEnabledContentionStop ()) + return true; -void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) -{ - SYSTEMTIME value; - GetSystemTime (&value); + FireEtwContentionStop ( + 0 /* ManagedContention */, + clr_instance_get_id (), + NULL, + NULL); - EP_ASSERT (system_time != NULL); - ep_system_time_set ( - system_time, - value.wYear, - value.wMonth, - value.wDayOfWeek, - value.wDay, - value.wHour, - value.wMinute, - value.wSecond, - value.wMilliseconds); + return true; } -int64_t -ep_rt_mono_system_timestamp_get (void) +bool +ep_rt_mono_write_event_method_jit_memory_allocated_for_code ( + const uint8_t *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) { - FILETIME value; - GetSystemTimeAsFileTime (&value); - return (int64_t)((((uint64_t)value.dwHighDateTime) << 32) | (uint64_t)value.dwLowDateTime); -} -#else -#include -#include -#include -#include - -#if HAVE_SYS_TIME_H -#include -#endif // HAVE_SYS_TIME_H - -#if HAVE_MACH_ABSOLUTE_TIME -#include -static mono_lazy_init_t _ep_rt_mono_time_base_info_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; -static mach_timebase_info_data_t _ep_rt_mono_time_base_info = {0}; -#endif + if (!EventEnabledMethodJitMemoryAllocatedForCode ()) + return true; -#ifdef HAVE_LOCALTIME_R -#define HAVE_GMTIME_R 1 -#endif + if (type != MONO_PROFILER_CODE_BUFFER_METHOD) + return true; -static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; -static const int64_t SECS_TO_100NS = 10000000; -static const int64_t SECS_TO_NS = 1000000000; -static const int64_t MSECS_TO_MIS = 1000; + uint64_t method_id = 0; + uint64_t module_id = 0; -/* clock_gettime () is found by configure on Apple builds, but its only present from ios 10, macos 10.12, tvos 10 and watchos 3 */ -#if defined (HAVE_CLOCK_MONOTONIC) && (defined(TARGET_IOS) || defined(TARGET_OSX) || defined(TARGET_WATCHOS) || defined(TARGET_TVOS)) -#undef HAVE_CLOCK_MONOTONIC -#endif + if (data) { + MonoMethod *method; + method = (MonoMethod *)data; + method_id = (uint64_t)method; + if (method->klass) + module_id = (uint64_t)(uint64_t)m_class_get_image (method->klass); + } -#ifndef HAVE_CLOCK_MONOTONIC -static const int64_t MISECS_TO_NS = 1000; -#endif + FireEtwMethodJitMemoryAllocatedForCode ( + method_id, + module_id, + size, + 0, + size, + 0 /* CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN */, + clr_instance_get_id (), + NULL, + NULL); -static -void -time_base_info_lazy_init (void); + return true; +} -static -int64_t -system_time_to_int64 ( - time_t sec, - long nsec); +bool +ep_rt_write_event_threadpool_worker_thread_start ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadStart ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} -#if HAVE_MACH_ABSOLUTE_TIME -static -void -time_base_info_lazy_init (void) +bool +ep_rt_write_event_threadpool_worker_thread_stop ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) { - kern_return_t result = mach_timebase_info (&_ep_rt_mono_time_base_info); - if (result != KERN_SUCCESS) - memset (&_ep_rt_mono_time_base_info, 0, sizeof (_ep_rt_mono_time_base_info)); + return FireEtwThreadPoolWorkerThreadStop ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -#endif -int64_t -ep_rt_mono_perf_counter_query (void) +bool +ep_rt_write_event_threadpool_worker_thread_wait ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) { -#if HAVE_MACH_ABSOLUTE_TIME - return (int64_t)mach_absolute_time (); -#elif HAVE_CLOCK_MONOTONIC - struct timespec ts; - int result = clock_gettime (CLOCK_MONOTONIC, &ts); - if (result == 0) - return ((int64_t)(ts.tv_sec) * (int64_t)(SECS_TO_NS)) + (int64_t)(ts.tv_nsec); -#else - #error "ep_rt_mono_perf_counter_get requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." -#endif - return 0; + return FireEtwThreadPoolWorkerThreadWait ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -int64_t -ep_rt_mono_perf_frequency_query (void) +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_sample ( + double throughput, + uint16_t clr_instance_id) { -#if HAVE_MACH_ABSOLUTE_TIME - // (numer / denom) gives you the nanoseconds per tick, so the below code - // computes the number of ticks per second. We explicitly do the multiplication - // first in order to help minimize the error that is produced by integer division. - mono_lazy_initialize (&_ep_rt_mono_time_base_info_init, time_base_info_lazy_init); - if (_ep_rt_mono_time_base_info.denom == 0 || _ep_rt_mono_time_base_info.numer == 0) - return 0; - return ((int64_t)(SECS_TO_NS) * (int64_t)(_ep_rt_mono_time_base_info.denom)) / (int64_t)(_ep_rt_mono_time_base_info.numer); -#elif HAVE_CLOCK_MONOTONIC - // clock_gettime () returns a result in terms of nanoseconds rather than a count. This - // means that we need to either always scale the result by the actual resolution (to - // get a count) or we need to say the resolution is in terms of nanoseconds. We prefer - // the latter since it allows the highest throughput and should minimize error propagated - // to the user. - return (int64_t)(SECS_TO_NS); -#else - #error "ep_rt_mono_perf_frequency_query requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." -#endif - return 0; + return FireEtwThreadPoolWorkerThreadAdjustmentSample ( + throughput, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_adjustment ( + double average_throughput, + uint32_t networker_thread_count, + /*NativeRuntimeEventSource.ThreadAdjustmentReasonMap*/ int32_t reason, + uint16_t clr_instance_id) { - time_t tt; -#if HAVE_GMTIME_R - struct tm ut; -#endif /* HAVE_GMTIME_R */ - struct tm *ut_ptr; - struct timeval time_val; - int timeofday_retval; - - EP_ASSERT (system_time != NULL); - - tt = time (NULL); - - /* We can't get millisecond resolution from time (), so we get it from gettimeofday () */ - timeofday_retval = gettimeofday (&time_val, NULL); - -#if HAVE_GMTIME_R - ut_ptr = &ut; - if (gmtime_r (&tt, ut_ptr) == NULL) -#else /* HAVE_GMTIME_R */ - if ((ut_ptr = gmtime (&tt)) == NULL) -#endif /* HAVE_GMTIME_R */ - EP_UNREACHABLE (); - - uint16_t milliseconds = 0; - if (timeofday_retval != -1) { - int old_seconds; - int new_seconds; - - milliseconds = time_val.tv_usec / MSECS_TO_MIS; - - old_seconds = ut_ptr->tm_sec; - new_seconds = time_val.tv_sec % 60; - - /* just in case we reached the next second in the interval between time () and gettimeofday () */ - if (old_seconds != new_seconds) - milliseconds = 999; - } + return FireEtwThreadPoolWorkerThreadAdjustmentAdjustment ( + average_throughput, + networker_thread_count, + reason, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} - ep_system_time_set ( - system_time, - 1900 + ut_ptr->tm_year, - ut_ptr->tm_mon + 1, - ut_ptr->tm_wday, - ut_ptr->tm_mday, - ut_ptr->tm_hour, - ut_ptr->tm_min, - ut_ptr->tm_sec, - milliseconds); +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_stats ( + double duration, + double throughput, + double threadpool_worker_thread_wait, + double throughput_wave, + double throughput_error_estimate, + double average_throughput_error_estimate, + double throughput_ratio, + double confidence, + double new_control_setting, + uint16_t new_thread_wave_magnitude, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadAdjustmentStats ( + duration, + throughput, + threadpool_worker_thread_wait, + throughput_wave, + throughput_error_estimate, + average_throughput_error_estimate, + throughput_ratio, + confidence, + new_control_setting, + new_thread_wave_magnitude, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -static -inline -int64_t -system_time_to_int64 ( - time_t sec, - long nsec) +bool +ep_rt_write_event_threadpool_io_enqueue ( + intptr_t native_overlapped, + intptr_t overlapped, + bool multi_dequeues, + uint16_t clr_instance_id) { - return ((int64_t)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + (nsec / 100); + return FireEtwThreadPoolIOEnqueue ( + (const void *)native_overlapped, + (const void *)overlapped, + multi_dequeues, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -int64_t -ep_rt_mono_system_timestamp_get (void) +bool +ep_rt_write_event_threadpool_io_dequeue ( + intptr_t native_overlapped, + intptr_t overlapped, + uint16_t clr_instance_id) { -#if HAVE_CLOCK_MONOTONIC - struct timespec time; - if (clock_gettime (CLOCK_REALTIME, &time) == 0) - return system_time_to_int64 (time.tv_sec, time.tv_nsec); -#else - struct timeval time; - if (gettimeofday (&time, NULL) == 0) - return system_time_to_int64 (time.tv_sec, time.tv_usec * MISECS_TO_NS); -#endif - else - return system_time_to_int64 (0, 0); + return FireEtwThreadPoolIODequeue ( + (const void *)native_overlapped, + (const void *)overlapped, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; } -#endif -#ifndef HOST_WIN32 -#if defined(__APPLE__) -#if defined (TARGET_OSX) -G_BEGIN_DECLS -gchar ***_NSGetEnviron(void); -G_END_DECLS -#define environ (*_NSGetEnviron()) -#else -static char *_ep_rt_mono_environ[1] = { NULL }; -#define environ _ep_rt_mono_environ -#endif /* defined (TARGET_OSX) */ -#else -G_BEGIN_DECLS -extern char **environ; -G_END_DECLS -#endif /* defined (__APPLE__) */ -#endif /* !defined (HOST_WIN32) */ +bool +ep_rt_write_event_threadpool_working_thread_count ( + uint16_t count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkingThreadCount ( + count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} +static void -ep_rt_mono_os_environment_get_utf16 (ep_rt_env_array_utf16_t *env_array) +runtime_profiler_jit_begin ( + MonoProfiler *prof, + MonoMethod *method) { - EP_ASSERT (env_array != NULL); -#ifdef HOST_WIN32 - LPWSTR envs = GetEnvironmentStringsW (); - if (envs) { - LPWSTR next = envs; - while (*next) { - ep_rt_env_array_utf16_append (env_array, ep_rt_utf16_string_dup (next)); - next += ep_rt_utf16_string_len (next) + 1; - } - FreeEnvironmentStringsW (envs); - } -#else - gchar **next = NULL; - for (next = environ; *next != NULL; ++next) - ep_rt_env_array_utf16_append (env_array, ep_rt_utf8_to_utf16_string (*next, -1)); -#endif + ep_rt_mono_write_event_jit_start (method); +} + +static +void +runtime_profiler_jit_failed ( + MonoProfiler *prof, + MonoMethod *method) +{ + //TODO: CoreCLR doesn't have this case, so no failure event currently exists. } +static void -ep_rt_mono_init_providers_and_events (void) +runtime_profiler_jit_done ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji) { - extern void InitProvidersAndEvents (void); - InitProvidersAndEvents (); + ep_rt_mono_write_event_method_load (method, ji); + ep_rt_mono_write_event_method_il_to_native_map (method, ji); } +static void -ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config) +runtime_profiler_image_loaded ( + MonoProfiler *prof, + MonoImage *image) { - if (!ep_rt_utf8_string_compare (ep_config_get_rundown_provider_name_utf8 (), ep_provider_config_get_provider_name (provider_config))) { - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = ep_provider_config_get_logging_level (provider_config); - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = ep_provider_config_get_keywords (provider_config); - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = true; - } + if (image && image->heap_pdb.size == 0) + ep_rt_mono_write_event_module_load (image); } -bool -ep_rt_mono_providers_validate_all_disabled (void) +static +void +runtime_profiler_image_unloaded ( + MonoProfiler *prof, + MonoImage *image) { - return (!MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled); + if (image && image->heap_pdb.size == 0) + ep_rt_mono_write_event_module_unload (image); } +static void -ep_rt_mono_fini_providers_and_events (void) +runtime_profiler_assembly_loaded ( + MonoProfiler *prof, + MonoAssembly *assembly) { - // dotnet/runtime: issue 12775: EventPipe shutdown race conditions - // Deallocating providers/events here might cause AV if a WriteEvent - // was to occur. Thus, we are not doing this cleanup. + ep_rt_mono_write_event_assembly_load (assembly); } -bool -ep_rt_mono_walk_managed_stack_for_thread ( - ep_rt_thread_handle_t thread, - EventPipeStackContents *stack_contents) +static +void +runtime_profiler_assembly_unloaded ( + MonoProfiler *prof, + MonoAssembly *assembly) { - EP_ASSERT (thread != NULL && stack_contents != NULL); - - EventPipeStackWalkData stack_walk_data; - stack_walk_data.stack_contents = stack_contents; - stack_walk_data.top_frame = true; - stack_walk_data.async_frame = false; - stack_walk_data.safe_point_frame = false; - stack_walk_data.runtime_invoke_frame = false; - - if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); - else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); - - return true; + ep_rt_mono_write_event_assembly_unload (assembly); } -bool -ep_rt_mono_method_get_simple_assembly_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len) +static +void +runtime_profiler_thread_started ( + MonoProfiler *prof, + uintptr_t tid) { - EP_ASSERT (method != NULL); - EP_ASSERT (name != NULL); - - MonoClass *method_class = mono_method_get_class (method); - MonoImage *method_image = method_class ? mono_class_get_image (method_class) : NULL; - const ep_char8_t *assembly_name = method_image ? mono_image_get_name (method_image) : NULL; - - if (!assembly_name) - return false; - - g_strlcpy (name, assembly_name, name_len); - return true; + ep_rt_mono_write_event_thread_created (ep_rt_uint64_t_to_thread_id_t (tid)); } -bool -ep_rt_mono_method_get_full_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len) +static +void +runtime_profiler_thread_stopped ( + MonoProfiler *prof, + uintptr_t tid) { - EP_ASSERT (method != NULL); - EP_ASSERT (name != NULL); - - char *full_method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); - if (!full_method_name) - return false; - - g_strlcpy (name, full_method_name, name_len); - - g_free (full_method_name); - return true; + ep_rt_mono_write_event_thread_terminated (ep_rt_uint64_t_to_thread_id_t (tid)); } -bool -ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( - ep_rt_thread_handle_t sampling_thread, - EventPipeEvent *sampling_event) +static +void +runtime_profiler_class_loading ( + MonoProfiler *prof, + MonoClass *klass) { - // Follows CoreClr implementation of sample profiler. Generic invasive/expensive way to do CPU sample profiling relying on STW and stackwalks. - // TODO: Investigate alternatives on platforms supporting Signals/SuspendThread (see Mono profiler) or CPU PMU's (see ETW/perf_event_open). - - // Sample profiler only runs on one thread, no need to synchorinize. - if (!_ep_rt_mono_sampled_thread_callstacks) - _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileStackWalkData), _ep_rt_mono_max_sampled_thread_count); - - // Make sure there is room based on previous max number of sampled threads. - // NOTE, there is a chance there are more threads than max, if that's the case we will - // miss those threads in this sample, but will be included in next when max has been adjusted. - g_array_set_size (_ep_rt_mono_sampled_thread_callstacks, _ep_rt_mono_max_sampled_thread_count); - - uint32_t filtered_thread_count = 0; - uint32_t sampled_thread_count = 0; - - mono_stop_world (MONO_THREAD_INFO_FLAGS_NO_GC); - - gboolean async_context = mono_thread_info_is_async_context (); - mono_thread_info_set_is_async_context (TRUE); - - // Record all info needed in sample events while runtime is suspended, must be async safe. - FOREACH_THREAD_SAFE_EXCLUDE (thread_info, MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE) { - if (!mono_thread_info_is_running (thread_info)) { - MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); - if (thread_state->valid) { - if (sampled_thread_count < _ep_rt_mono_max_sampled_thread_count) { - EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, sampled_thread_count); - data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); - data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); - data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; - data->stack_walk_data.stack_contents = &data->stack_contents; - data->stack_walk_data.top_frame = true; - data->stack_walk_data.async_frame = false; - data->stack_walk_data.safe_point_frame = false; - data->stack_walk_data.runtime_invoke_frame = false; - ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); - if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { - // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame - // as second, re-classify current callstack to be executing managed code. - data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - } - - sampled_thread_count++; - } - } - } - filtered_thread_count++; - } FOREACH_THREAD_SAFE_END - - mono_thread_info_set_is_async_context (async_context); - mono_restart_world (MONO_THREAD_INFO_FLAGS_NO_GC); - - // Fire sample event for threads. Must be done after runtime is resumed since it's not async safe. - // Since we can't keep thread info around after runtime as been suspended, use an empty - // adapter instance and only set recorded tid as parameter inside adapter. - THREAD_INFO_TYPE adapter = { { 0 } }; - for (uint32_t i = 0; i < sampled_thread_count; ++i) { - EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, i); - if (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length(&data->stack_contents) > 0) { - // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. - // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event - // for this specific stackwalk or we could cleanup stack_frames before writing profile event. - if (data->stack_walk_data.async_frame) { - for (int i = 0; i < data->stack_contents.next_available_frame; ++i) - mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [i], TRUE, FALSE); - } - mono_thread_info_set_tid (&adapter, ep_rt_uint64_t_to_thread_id_t (data->thread_id)); - ep_write_sample_profile_event (sampling_thread, sampling_event, &adapter, &data->stack_contents, (uint8_t *)&data->payload_data, sizeof (data->payload_data)); - } - } - - // Current thread count will be our next maximum sampled threads. - _ep_rt_mono_max_sampled_thread_count = filtered_thread_count; - - return true; + ep_rt_mono_write_event_type_load_start (m_class_get_byval_arg (klass)); } +static void -ep_rt_mono_execute_rundown (ep_rt_execution_checkpoint_array_t *execution_checkpoints) +runtime_profiler_class_failed ( + MonoProfiler *prof, + MonoClass *klass) { - ep_char8_t runtime_module_path [256]; - const uint8_t object_guid [EP_GUID_SIZE] = { 0 }; - const uint16_t runtime_product_qfe_version = 0; - const uint32_t startup_flags = 0; - const uint8_t startup_mode = 0; - const ep_char8_t *command_line = ""; + ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); +} - if (!g_module_address ((void *)mono_init, runtime_module_path, sizeof (runtime_module_path), NULL, NULL, 0, NULL)) - runtime_module_path [0] = '\0'; +static +void +runtime_profiler_class_loaded ( + MonoProfiler *prof, + MonoClass *klass) +{ + ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); +} - FireEtwRuntimeInformationDCStart ( - clr_instance_get_id (), - RUNTIME_SKU_CORECLR, - RuntimeProductMajorVersion, - RuntimeProductMinorVersion, - RuntimeProductPatchVersion, - runtime_product_qfe_version, - RuntimeFileMajorVersion, - RuntimeFileMajorVersion, - RuntimeFileBuildVersion, - RuntimeFileRevisionVersion, - startup_mode, - startup_flags, - command_line, - object_guid, - runtime_module_path, - NULL, - NULL); +static +void +runtime_profiler_exception_throw ( + MonoProfiler *prof, + MonoObject *exc) +{ + ep_rt_mono_write_event_exception_thrown (exc); +} - if (execution_checkpoints) { - ep_rt_execution_checkpoint_array_iterator_t execution_checkpoints_iterator = ep_rt_execution_checkpoint_array_iterator_begin (execution_checkpoints); - while (!ep_rt_execution_checkpoint_array_iterator_end (execution_checkpoints, &execution_checkpoints_iterator)) { - EventPipeExecutionCheckpoint *checkpoint = ep_rt_execution_checkpoint_array_iterator_value (&execution_checkpoints_iterator); - FireEtwExecutionCheckpointDCEnd ( - clr_instance_get_id (), - checkpoint->name, - checkpoint->timestamp, - NULL, - NULL); - ep_rt_execution_checkpoint_array_iterator_next (&execution_checkpoints_iterator); - } - } +static +void +runtime_profiler_exception_clause ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc) +{ + ep_rt_mono_write_event_exception_clause (method, clause_num, clause_type, exc); +} - FireEtwDCEndInit_V1 ( - clr_instance_get_id (), - NULL, - NULL); +static +void +runtime_profiler_monitor_contention ( + MonoProfiler *prof, + MonoObject *obj) +{ + ep_rt_mono_write_event_monitor_contention_start (obj); +} - eventpipe_execute_rundown ( - fire_domain_rundown_events_func, - fire_assembly_rundown_events_func, - fire_method_rundown_events_func); +static +void +runtime_profiler_monitor_acquired ( + MonoProfiler *prof, + MonoObject *obj) +{ + ep_rt_mono_write_event_monitor_contention_stop (obj); +} - FireEtwDCEndComplete_V1 ( - clr_instance_get_id (), - NULL, - NULL); +static +void +runtime_profiler_monitor_failed ( + MonoProfiler *prof, + MonoObject *obj) +{ + ep_rt_mono_write_event_monitor_contention_stop (obj); } -bool -ep_rt_mono_write_event_ee_startup_start (void) +static +void +runtime_profiler_jit_code_buffer ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) { - return FireEtwEEStartupStart_V1 ( - clr_instance_get_id (), - NULL, - NULL); + ep_rt_mono_write_event_method_jit_memory_allocated_for_code ((const uint8_t *)buffer, size, type, data); } -bool -ep_rt_mono_write_event_jit_start (MonoMethod *method) +void +EventPipeEtwCallbackDotNETRuntime ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) { - if (!EventEnabledMethodJittingStarted_V1 ()) - return true; + ep_rt_config_requires_lock_not_held (); - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. - if (method) { - uint64_t method_id = 0; - uint64_t module_id = 0; - uint32_t code_size = 0; - uint32_t method_token = 0; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; + EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; + EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); - //TODO: SendMethodDetailsEvent + match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; - method_id = (uint64_t)method; + EP_LOCK_ENTER (section1) + uint64_t enabled_keywords = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - if (!method->dynamic) - method_token = method->token; + if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_begin); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_failed); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_done); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } - if (!mono_method_has_no_body (method)) { - ERROR_DECL (error); - MonoMethodHeader *header = mono_method_get_header_internal (method, error); - if (header) - code_size = header->code_size; + if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_loaded); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_unloaded); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_loaded); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_unloaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } } - method_name = method->name; - method_signature = mono_signature_full_name (method->signature); + if (profiler_callback_is_enabled(match_any_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_started); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_stopped); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + if (profiler_callback_is_enabled(match_any_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loading); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_failed); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } } - FireEtwMethodJittingStarted_V1 ( - method_id, - module_id, - method_token, - code_size, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); + if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_throw); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_clause); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } - g_free (method_namespace); - g_free (method_signature); - } + if (profiler_callback_is_enabled(match_any_keywords, CONTENTION_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_contention); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_acquired); + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_failed); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } - return true; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + EP_LOCK_EXIT (section1) + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); } -bool -ep_rt_mono_write_event_method_il_to_native_map ( - MonoMethod *method, - MonoJitInfo *ji) +void +EventPipeEtwCallbackDotNETRuntimeRundown ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) { - if (!EventEnabledMethodILToNativeMap ()) - return true; + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); +} - if (method) { - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); +void +EventPipeEtwCallbackDotNETRuntimePrivate ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); +} - uint64_t method_id = (uint64_t)method; - uint32_t fixed_buffer [64]; - uint8_t *buffer = NULL; +void +EventPipeEtwCallbackDotNETRuntimeStress ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); +} - uint16_t offset_entries = 0; - uint32_t *il_offsets = NULL; - uint32_t *native_offsets = NULL; +static +void +mono_profiler_app_domain_loading ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoading ()) + return; - MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; - if (debug_info) { - if (offset_entries != 0) { - offset_entries = debug_info->num_line_numbers; - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (needed_size > sizeof (fixed_buffer)) { - buffer = g_new (uint8_t, needed_size); - il_offsets = (uint32_t*)buffer; - } else { - il_offsets = fixed_buffer; - } - if (il_offsets) { - native_offsets = il_offsets + offset_entries; - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; - } - } - } + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainLoading ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} - mono_debug_free_method_jit_info (debug_info); - } +static +void +mono_profiler_app_domain_loaded ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoaded ()) + return; - if (!il_offsets && !native_offsets) { - // No IL offset -> Native offset mapping available. Put all code on IL offset 0. - EP_ASSERT (sizeof (fixed_buffer) >= sizeof (uint32_t) * 2); - offset_entries = 1; - il_offsets = fixed_buffer; - native_offsets = il_offsets + offset_entries; - il_offsets [0] = 0; - native_offsets [0] = ji ? (uint32_t)ji->code_size : 0; - } + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainLoaded ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_app_domain_unloading ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloading ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainUnloading ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} - FireEtwMethodILToNativeMap ( - method_id, - 0, - 0, - offset_entries, - il_offsets, - native_offsets, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_app_domain_unloaded ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloaded ()) + return; - g_free (buffer); - } + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainUnloaded ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} - return true; +static +void +mono_profiler_app_domain_name ( + MonoProfiler *prof, + MonoDomain *domain, + const char *name) +{ + if (!EventEnabledMonoProfilerAppDomainName ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainName ( + clr_instance_get_id (), + domain_id, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); } -bool -ep_rt_mono_write_event_method_load ( +static +inline +void +get_jit_data ( MonoMethod *method, - MonoJitInfo *ji) + uint64_t *method_id, + uint64_t *module_id, + uint32_t *method_token) { - if (!EventEnabledMethodLoad_V1 () && !EventEnabledMethodLoadVerbose_V1()) - return true; + *method_id = (uint64_t)method; + *module_id = 0; + *method_token = 0; - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. if (method) { - uint64_t method_id = 0; - uint64_t module_id = 0; - uint64_t method_code_start = ji ? (uint64_t)ji->code_start : 0; - uint32_t method_code_size = ji ? (uint32_t)ji->code_size : 0; - uint32_t method_token = 0; - uint32_t method_flags = 0; - uint8_t kind = MONO_CLASS_DEF; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); - - method_id = (uint64_t)method; + *method_token = method->token; + if (method->klass) + *module_id = (uint64_t)m_class_get_image (method->klass); + } +} - if (!method->dynamic) - method_token = method->token; +static +void +mono_profiler_jit_begin ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitBegin()) + return; - if (ji && mono_jit_info_get_generic_sharing_context (ji)) { - method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; - verbose = true; - } + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; - if (method->dynamic) { - method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; - verbose = true; - } + get_jit_data (method, &method_id, &module_id, &method_token); - if (ji && !ji->from_aot && !ji->from_llvm) { - method_flags |= METHOD_FLAGS_JITTED_METHOD; - if (method->wrapper_type != MONO_WRAPPER_NONE) - method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; - } + FireEtwMonoProfilerJitBegin ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} - if (method->is_generic || method->is_inflated) { - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - verbose = true; - } +static +void +mono_profiler_jit_failed ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitFailed()) + return; - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - kind = m_class_get_class_kind (method->klass); - if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - } + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; - //TODO: SendMethodDetailsEvent + get_jit_data (method, &method_id, &module_id, &method_token); - if (verbose) { - method_name = method->name; - method_signature = mono_signature_full_name (method->signature); + FireEtwMonoProfilerJitFailed ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} - if (method->klass) - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); +static +void +mono_profiler_jit_done ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMonoProfilerJitDone()) + return; - FireEtwMethodLoadVerbose_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; - if (ji && (ji->from_aot || ji->from_llvm)) - FireEtwMethodLoadVerbose_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - } else { - FireEtwMethodLoad_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - clr_instance_get_id (), - NULL, - NULL); + get_jit_data (method, &method_id, &module_id, &method_token); - if (ji && (ji->from_aot || ji->from_llvm)) - FireEtwMethodLoad_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - clr_instance_get_id (), - NULL, - NULL); - } + FireEtwMonoProfilerJitDone ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} - g_free (method_namespace); - g_free (method_signature); - } +static +void +mono_profiler_jit_chunk_created ( + MonoProfiler *prof, + const mono_byte *chunk, + uintptr_t size) +{ + if (!EventEnabledMonoProfilerJitChunkCreated()) + return; - return true; + FireEtwMonoProfilerJitChunkCreated ( + clr_instance_get_id (), + chunk, + (uint64_t)size, + NULL, + NULL); } static -bool -get_module_event_data ( - MonoImage *image, - ModuleEventData *module_data) +void +mono_profiler_jit_chunk_destroyed ( + MonoProfiler *prof, + const mono_byte *chunk) { - if (image && module_data) { - memset (module_data->signature, 0, EP_GUID_SIZE); + if (!EventEnabledMonoProfilerJitChunkDestroyed()) + return; - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); + FireEtwMonoProfilerJitChunkDestroyed ( + clr_instance_get_id (), + chunk, + NULL, + NULL); +} - module_data->domain_id = (uint64_t)root_domain; - module_data->module_id = (uint64_t)image; - module_data->assembly_id = (uint64_t)image->assembly; +static +void +mono_profiler_jit_code_buffer ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) +{ + if (!EventEnabledMonoProfilerJitCodeBuffer()) + return; - // TODO: Extract all module IL/Native paths and pdb metadata when available. - module_data->module_il_path = ""; - module_data->module_il_pdb_path = ""; - module_data->module_native_path = ""; - module_data->module_native_pdb_path = ""; + FireEtwMonoProfilerJitCodeBuffer ( + clr_instance_get_id (), + buffer, + size, + (uint8_t)type, + NULL, + NULL); +} - module_data->module_il_pdb_age = 0; - module_data->module_native_pdb_age = 0; +static +inline +void +get_class_data ( + MonoClass *klass, + uint64_t *class_id, + uint64_t *module_id, + ep_char8_t **class_name) +{ + *class_id = (uint64_t)klass; + *module_id = 0; - module_data->reserved_flags = 0; + if (klass) + *module_id = (uint64_t)m_class_get_image (klass); - // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. - module_data->module_flags = MODULE_FLAGS_MANIFEST_MODULE; - if (image->dynamic) - module_data->module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; - if (image->aot_module) - module_data->module_flags |= MODULE_FLAGS_NATIVE_MODULE; + if (klass && class_name) + *class_name = (ep_char8_t *)mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_IL); + else if (class_name) + *class_name = NULL; +} - module_data->module_il_path = image->filename ? image->filename : ""; - } +static +void +mono_profiler_class_loading ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassLoading()) + return; - return true; + uint64_t class_id; + uint64_t module_id; + + get_class_data (klass, &class_id, &module_id, NULL); + + FireEtwMonoProfilerClassLoading ( + clr_instance_get_id (), + class_id, + module_id, + NULL, + NULL); } -bool -ep_rt_mono_write_event_module_load (MonoImage *image) +static +void +mono_profiler_class_failed ( + MonoProfiler *prof, + MonoClass *klass) { - if (!EventEnabledModuleLoad_V2 () && !EventEnabledDomainModuleLoad_V1()) - return true; + if (!EventEnabledMonoProfilerClassFailed()) + return; - if (image) { - ModuleEventData module_data; - if (get_module_event_data (image, &module_data)) { - FireEtwModuleLoad_V2 ( - module_data.module_id, - module_data.assembly_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - module_data.signature, - module_data.module_il_pdb_age, - module_data.module_il_pdb_path, - module_data.signature, - module_data.module_native_pdb_age, - module_data.module_native_pdb_path, - NULL, - NULL); + uint64_t class_id; + uint64_t module_id; - FireEtwDomainModuleLoad_V1 ( - module_data.module_id, - module_data.assembly_id, - module_data.domain_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - NULL, - NULL); - } - } + get_class_data (klass, &class_id, &module_id, NULL); - return true; + FireEtwMonoProfilerClassFailed ( + clr_instance_get_id (), + class_id, + module_id, + NULL, + NULL); } -bool -ep_rt_mono_write_event_module_unload (MonoImage *image) +static +void +mono_profiler_class_loaded ( + MonoProfiler *prof, + MonoClass *klass) { - if (!EventEnabledModuleUnload_V2()) - return true; + if (!EventEnabledMonoProfilerClassLoaded()) + return; - if (image) { - ModuleEventData module_data; - if (get_module_event_data (image, &module_data)) { - FireEtwModuleUnload_V2 ( - module_data.module_id, - module_data.assembly_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - module_data.signature, - module_data.module_il_pdb_age, - module_data.module_il_pdb_path, - module_data.signature, - module_data.module_native_pdb_age, - module_data.module_native_pdb_path, - NULL, - NULL); - } - } + uint64_t class_id; + uint64_t module_id; + ep_char8_t *class_name; - return true; + get_class_data (klass, &class_id, &module_id, &class_name); + + FireEtwMonoProfilerClassLoaded ( + clr_instance_get_id (), + class_id, + module_id, + class_name ? class_name : "", + NULL, + NULL); + + g_free (class_name); } static -bool -get_assembly_event_data ( - MonoAssembly *assembly, - AssemblyEventData *assembly_data) +inline +void +get_vtable_data ( + MonoVTable *vtable, + uint64_t *vtable_id, + uint64_t *class_id, + uint64_t *domain_id) { - if (assembly && assembly_data) { - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); + *vtable_id = (uint64_t)vtable; + *class_id = 0; + *domain_id = 0; - assembly_data->domain_id = (uint64_t)root_domain; - assembly_data->assembly_id = (uint64_t)assembly; - assembly_data->binding_id = 0; + if (vtable) { + *class_id = (uint64_t)mono_vtable_class_internal (vtable); + *domain_id = (uint64_t)mono_vtable_domain_internal (vtable); + } +} - assembly_data->assembly_flags = 0; - if (assembly->dynamic) - assembly_data->assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; +static +void +mono_profiler_vtable_loading ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableLoading()) + return; - if (assembly->image && assembly->image->aot_module) - assembly_data->assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; - assembly_data->assembly_name = mono_stringify_assembly_name (&assembly->aname); - } + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); - return true; + FireEtwMonoProfilerVTableLoading ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); } -bool -ep_rt_mono_write_event_assembly_load (MonoAssembly *assembly) +static +void +mono_profiler_vtable_failed ( + MonoProfiler *prof, + MonoVTable *vtable) { - if (!EventEnabledAssemblyLoad_V1 ()) - return true; - - if (assembly) { - AssemblyEventData assembly_data; - if (get_assembly_event_data (assembly, &assembly_data)) { - FireEtwAssemblyLoad_V1 ( - assembly_data.assembly_id, - assembly_data.domain_id, - assembly_data.binding_id, - assembly_data.assembly_flags, - assembly_data.assembly_name, - clr_instance_get_id (), - NULL, - NULL); + if (!EventEnabledMonoProfilerVTableFailed()) + return; - g_free (assembly_data.assembly_name); - } - } + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; - return true; + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + FireEtwMonoProfilerVTableFailed ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); } -bool -ep_rt_mono_write_event_assembly_unload (MonoAssembly *assembly) +static +void +mono_profiler_vtable_loaded ( + MonoProfiler *prof, + MonoVTable *vtable) { - if (!EventEnabledAssemblyUnload_V1 ()) - return true; - - if (assembly) { - AssemblyEventData assembly_data; - if (get_assembly_event_data (assembly, &assembly_data)) { - FireEtwAssemblyUnload_V1 ( - assembly_data.assembly_id, - assembly_data.domain_id, - assembly_data.binding_id, - assembly_data.assembly_flags, - assembly_data.assembly_name, - clr_instance_get_id (), - NULL, - NULL); + if (!EventEnabledMonoProfilerVTableLoaded()) + return; - g_free (assembly_data.assembly_name); - } - } + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; - return true; + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + FireEtwMonoProfilerVTableLoaded ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); } -bool -ep_rt_mono_write_event_thread_created (ep_rt_thread_id_t tid) +static +void +mono_profiler_module_loading ( + MonoProfiler *prof, + MonoImage *image) { - if (!EventEnabledThreadCreated ()) - return true; - - uint64_t managed_thread = 0; - uint64_t native_thread_id = ep_rt_thread_id_t_to_uint64_t (tid); - uint64_t managed_thread_id = 0; - uint32_t flags = 0; + if (!EventEnabledMonoProfilerModuleLoading ()) + return; + + FireEtwMonoProfilerModuleLoading ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); +} - MonoThread *thread = mono_thread_current (); - if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) { - managed_thread_id = (uint64_t)mono_thread_get_managed_id (thread); - managed_thread = (uint64_t)thread; +static +void +mono_profiler_module_failed ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleFailed ()) + return; - switch (mono_thread_info_get_flags (thread->thread_info)) { - case MONO_THREAD_INFO_FLAGS_NO_GC: - case MONO_THREAD_INFO_FLAGS_NO_SAMPLE: - flags |= THREAD_FLAGS_GC_SPECIAL; - } + FireEtwMonoProfilerModuleFailed ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); +} - if (mono_gc_is_finalizer_thread (thread)) - flags |= THREAD_FLAGS_FINALIZER; +static +void +mono_profiler_module_loaded ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleLoaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; - if (thread->threadpool_thread) - flags |= THREAD_FLAGS_THREADPOOL_WORKER; + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) + module_path = (const ep_char8_t *)module_data.module_il_path; + module_guid = (const ep_char8_t *)mono_image_get_guid (image); } - FireEtwThreadCreated ( - managed_thread, - (uint64_t)mono_get_root_domain (), - flags, - managed_thread_id, - native_thread_id, + FireEtwMonoProfilerModuleLoaded ( clr_instance_get_id (), + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", NULL, NULL); +} - return true; +static +void +mono_profiler_module_unloading ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleUnloading ()) + return; + + FireEtwMonoProfilerModuleUnloading ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); } -bool -ep_rt_mono_write_event_thread_terminated (ep_rt_thread_id_t tid) +static +void +mono_profiler_module_unloaded ( + MonoProfiler *prof, + MonoImage *image) { - if (!EventEnabledThreadTerminated ()) - return true; + if (!EventEnabledMonoProfilerModuleUnloaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; - uint64_t managed_thread = 0; - MonoThread *thread = mono_thread_current (); - if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) - managed_thread = (uint64_t)thread; + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) + module_path = (const ep_char8_t *)module_data.module_il_path; + module_guid = (const ep_char8_t *)mono_image_get_guid (image); + } - FireEtwThreadTerminated ( - managed_thread, - (uint64_t)mono_get_root_domain (), + FireEtwMonoProfilerModuleUnloaded ( clr_instance_get_id (), + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", NULL, NULL); - - return true; } static -uint32_t -get_type_start_id (MonoType *type) +inline +void +get_assembly_data ( + MonoAssembly *assembly, + uint64_t *assembly_id, + uint64_t *module_id, + ep_char8_t **assembly_name) { - uint32_t start_id = (uint32_t)(uintptr_t)type; + *assembly_id = (uint64_t)assembly; + *module_id = 0; - start_id = (((start_id * 215497) >> 16) ^ ((start_id * 1823231) + start_id)); - - // Mix in highest bits on 64-bit systems only - if (sizeof (type) > 4) - start_id = start_id ^ (((uint64_t)type >> 31) >> 1); + if (assembly) + *module_id = (uint64_t)mono_assembly_get_image_internal (assembly); - return start_id; + if (assembly && assembly_name) + *assembly_name = (ep_char8_t *)mono_stringify_assembly_name (&assembly->aname); + else if (assembly_name) + *assembly_name = NULL; } -bool -ep_rt_mono_write_event_type_load_start (MonoType *type) +static +void +mono_profiler_assembly_loading ( + MonoProfiler *prof, + MonoAssembly *assembly) { - if (!EventEnabledTypeLoadStart ()) - return true; + if (!EventEnabledMonoProfilerAssemblyLoading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; - FireEtwTypeLoadStart ( - get_type_start_id (type), + get_assembly_data (assembly, &assembly_id, &module_id, NULL); + + FireEtwMonoProfilerAssemblyLoading ( clr_instance_get_id (), + assembly_id, + module_id, NULL, NULL); - - return true; } -bool -ep_rt_mono_write_event_type_load_stop (MonoType *type) +static +void +mono_profiler_assembly_loaded ( + MonoProfiler *prof, + MonoAssembly *assembly) { - if (!EventEnabledTypeLoadStop ()) - return true; + if (!EventEnabledMonoProfilerAssemblyLoaded ()) + return; - char *type_name = NULL; - if (type) - type_name = mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; - FireEtwTypeLoadStop ( - get_type_start_id (type), + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); + + FireEtwMonoProfilerAssemblyLoaded ( clr_instance_get_id (), - 6 /* CLASS_LOADED */, - (uint64_t)type, - type_name, + assembly_id, + module_id, + assembly_name ? assembly_name : "", NULL, NULL); - g_free (type_name); - - return true; + g_free (assembly_name); } static -gboolean -get_exception_ip_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) -{ - *(uintptr_t *)data = (uintptr_t)MONO_CONTEXT_GET_IP (ctx); - return TRUE; -} - -bool -ep_rt_mono_write_event_exception_thrown (MonoObject *obj) +void +mono_profiler_assembly_unloading ( + MonoProfiler *prof, + MonoAssembly *assembly) { - if (!EventEnabledExceptionThrown_V1 ()) - return true; - - if (obj) { - ERROR_DECL (error); - char *type_name = NULL; - char *exception_message = NULL; - uint16_t flags = 0; - uint32_t hresult = 0; - uintptr_t ip = 0; - - if (mono_object_isinst_checked ((MonoObject *) obj, mono_get_exception_class (), error)) { - MonoException *exception = (MonoException *)obj; - flags |= EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT; - if (exception->inner_ex) - flags |= EXCEPTION_THROWN_FLAGS_HAS_INNER; - exception_message = ep_rt_utf16_to_utf8_string (mono_string_chars_internal (exception->message), mono_string_length_internal (exception->message)); - hresult = exception->hresult; - } + if (!EventEnabledMonoProfilerAssemblyUnloading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; - if (mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_SIGNAL_SAFE, (void *)&ip); + get_assembly_data (assembly, &assembly_id, &module_id, NULL); - type_name = mono_type_get_name_full (m_class_get_byval_arg (mono_object_class (obj)), MONO_TYPE_NAME_FORMAT_IL); + FireEtwMonoProfilerAssemblyUnloading ( + clr_instance_get_id (), + assembly_id, + module_id, + NULL, + NULL); +} - FireEtwExceptionThrown_V1 ( - type_name, - exception_message, - (void *)&ip, - hresult, - flags, - clr_instance_get_id (), - NULL, - NULL); +static +void +mono_profiler_assembly_unloaded ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyUnloaded ()) + return; - if (!mono_component_profiler_clauses_enabled ()) { - FireEtwExceptionThrownStop ( - NULL, - NULL); - } + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; - g_free (exception_message); - g_free (type_name); + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); - mono_error_cleanup (error); - } + FireEtwMonoProfilerAssemblyUnloaded ( + clr_instance_get_id (), + assembly_id, + module_id, + assembly_name ? assembly_name : "", + NULL, + NULL); - return true; + g_free (assembly_name); } -bool -ep_rt_mono_write_event_exception_clause ( +static +void +mono_profiler_method_enter ( + MonoProfiler *prof, MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *obj) + MonoProfilerCallContext *context) { - if (!mono_component_profiler_clauses_enabled ()) - return true; - - if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE) && (!EventEnabledExceptionCatchStart() || !EventEnabledExceptionCatchStop())) - return true; - - if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER && (!EventEnabledExceptionFilterStart() || !EventEnabledExceptionFilterStop())) - return true; - - if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY && (!EventEnabledExceptionFinallyStart() || !EventEnabledExceptionFinallyStop())) - return true; - - uintptr_t ip = 0; //TODO: Have profiler pass along IP of handler block. - uint64_t method_id = (uint64_t)method; - char *method_name = NULL; - - method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); - - if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE)) { - FireEtwExceptionCatchStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); - - FireEtwExceptionCatchStop ( - NULL, - NULL); - - FireEtwExceptionThrownStop ( - NULL, - NULL); - } + if (!EventEnabledMonoProfilerMethodEnter ()) + return; - if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER) { - FireEtwExceptionFilterStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); + FireEtwMonoProfilerMethodEnter ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} - FireEtwExceptionFilterStop ( - NULL, - NULL); - } +static +void +mono_profiler_method_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context) +{ + if (!EventEnabledMonoProfilerMethodLeave ()) + return; - if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY) { - FireEtwExceptionFinallyStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); + FireEtwMonoProfilerMethodLeave ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} - FireEtwExceptionFinallyStop ( - NULL, - NULL); - } +static +void +mono_profiler_method_tail_call ( + MonoProfiler *prof, + MonoMethod *method, + MonoMethod *target_method) +{ + if (!EventEnabledMonoProfilerMethodTailCall ()) + return; - g_free (method_name); - return true; + FireEtwMonoProfilerMethodTailCall ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); } -bool -ep_rt_mono_write_event_monitor_contention_start (MonoObject *obj) +static +void +mono_profiler_method_exception_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoObject *exc) { - if (!EventEnabledContentionStart_V1 ()) - return true; + if (!EventEnabledMonoProfilerMethodExceptionLeave ()) + return; - FireEtwContentionStart_V1 ( - 0 /* ManagedContention */, + FireEtwMonoProfilerMethodExceptionLeave ( clr_instance_get_id (), + (uint64_t)method, NULL, NULL); - - return true; } -bool -ep_rt_mono_write_event_monitor_contention_stop (MonoObject *obj) +static +void +mono_profiler_method_free ( + MonoProfiler *prof, + MonoMethod *method) { - if (!EventEnabledContentionStop ()) - return true; + if (!EventEnabledMonoProfilerMethodFree ()) + return; - FireEtwContentionStop ( - 0 /* ManagedContention */, + FireEtwMonoProfilerMethodFree ( clr_instance_get_id (), + (uint64_t)method, NULL, NULL); - - return true; } -bool -ep_rt_mono_write_event_method_jit_memory_allocated_for_code ( - const uint8_t *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data) +static +void +mono_profiler_method_begin_invoke ( + MonoProfiler *prof, + MonoMethod *method) { - if (!EventEnabledMethodJitMemoryAllocatedForCode ()) - return true; - - if (type != MONO_PROFILER_CODE_BUFFER_METHOD) - return true; - - uint64_t method_id = 0; - uint64_t module_id = 0; - - if (data) { - MonoMethod *method; - method = (MonoMethod *)data; - method_id = (uint64_t)method; - if (method->klass) - module_id = (uint64_t)(uint64_t)m_class_get_image (method->klass); - } + if (!EventEnabledMonoProfilerMethodBeginInvoke ()) + return; - FireEtwMethodJitMemoryAllocatedForCode ( - method_id, - module_id, - size, - 0, - size, - 0 /* CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN */, + FireEtwMonoProfilerMethodBeginInvoke ( clr_instance_get_id (), + (uint64_t)method, NULL, NULL); - - return true; } -bool -ep_rt_write_event_threadpool_worker_thread_start ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) +static +void +mono_profiler_method_end_invoke ( + MonoProfiler *prof, + MonoMethod *method) { - return FireEtwThreadPoolWorkerThreadStart ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} + if (!EventEnabledMonoProfilerMethodEndInvoke ()) + return; -bool -ep_rt_write_event_threadpool_worker_thread_stop ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadStop ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, + FireEtwMonoProfilerMethodEndInvoke ( + clr_instance_get_id (), + (uint64_t)method, NULL, - NULL) == 0 ? true : false; + NULL); } -bool -ep_rt_write_event_threadpool_worker_thread_wait ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) +static +MonoProfilerCallInstrumentationFlags +mono_profiler_method_instrumentation ( + MonoProfiler *prof, + MonoMethod *method) { - return FireEtwThreadPoolWorkerThreadWait ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} + if (_ep_rt_dotnet_mono_profiler_provider_callspec.len > 0 && !mono_callspec_eval (method, &_ep_rt_dotnet_mono_profiler_provider_callspec)) + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_sample ( - double throughput, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadAdjustmentSample ( - throughput, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; + return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; } -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_adjustment ( - double average_throughput, - uint32_t networker_thread_count, - /*NativeRuntimeEventSource.ThreadAdjustmentReasonMap*/ int32_t reason, - uint16_t clr_instance_id) +static +void +mono_profiler_exception_throw ( + MonoProfiler *prof, + MonoObject *exc) { - return FireEtwThreadPoolWorkerThreadAdjustmentAdjustment ( - average_throughput, - networker_thread_count, - reason, - clr_instance_id, + if (!EventEnabledMonoProfilerExceptionThrow ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + FireEtwMonoProfilerExceptionThrow ( + clr_instance_get_id (), + type_id, + SGEN_POINTER_UNTAG_ALL (exc), NULL, - NULL) == 0 ? true : false; + NULL); } -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_stats ( - double duration, - double throughput, - double threadpool_worker_thread_wait, - double throughput_wave, - double throughput_error_estimate, - double average_throughput_error_estimate, - double throughput_ratio, - double confidence, - double new_control_setting, - uint16_t new_thread_wave_magnitude, - uint16_t clr_instance_id) +static +void +mono_profiler_exception_clause ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc) { - return FireEtwThreadPoolWorkerThreadAdjustmentStats ( - duration, - throughput, - threadpool_worker_thread_wait, - throughput_wave, - throughput_error_estimate, - average_throughput_error_estimate, - throughput_ratio, - confidence, - new_control_setting, - new_thread_wave_magnitude, - clr_instance_id, + if (!EventEnabledMonoProfilerExceptionClause ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + FireEtwMonoProfilerExceptionClause ( + clr_instance_get_id (), + (uint8_t)clause_type, + clause_num, + (uint64_t)method, + type_id, + SGEN_POINTER_UNTAG_ALL (exc), NULL, - NULL) == 0 ? true : false; + NULL); } -bool -ep_rt_write_event_threadpool_io_enqueue ( - intptr_t native_overlapped, - intptr_t overlapped, - bool multi_dequeues, - uint16_t clr_instance_id) +static +void +mono_profiler_gc_event ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial) { - return FireEtwThreadPoolIOEnqueue ( - (const void *)native_overlapped, - (const void *)overlapped, - multi_dequeues, - clr_instance_id, + if (!EventEnabledMonoProfilerGCEvent ()) + return; + + // TODO: Needs to be async safe. + /*FireEtwMonoProfilerGCEvent ( + clr_instance_get_id (), + (uint8_t)gc_event, + generation, NULL, - NULL) == 0 ? true : false; + NULL);*/ } -bool -ep_rt_write_event_threadpool_io_dequeue ( - intptr_t native_overlapped, - intptr_t overlapped, - uint16_t clr_instance_id) +static +void +mono_profiler_gc_allocation ( + MonoProfiler *prof, + MonoObject *object) { - return FireEtwThreadPoolIODequeue ( - (const void *)native_overlapped, - (const void *)overlapped, - clr_instance_id, + if (!EventEnabledMonoProfilerGCAllocation ()) + return; + + uint64_t vtable_id = 0; + uint64_t object_size = 0; + + if (object) { + vtable_id = (uint64_t)mono_object_get_vtable_internal (object); + object_size = (uint64_t)mono_object_get_size_internal (object); + + /* account for object alignment */ + object_size += 7; + object_size &= ~7; + } + + FireEtwMonoProfilerGCAllocation ( + clr_instance_get_id (), + vtable_id, + SGEN_POINTER_UNTAG_ALL (object), + object_size, NULL, - NULL) == 0 ? true : false; + NULL); } -bool -ep_rt_write_event_threadpool_working_thread_count ( - uint16_t count, - uint16_t clr_instance_id) +static +void +mono_profiler_gc_moves ( + MonoProfiler *prof, + MonoObject *const* objects, + uint64_t count) { - return FireEtwThreadPoolWorkingThreadCount ( - count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; + if (!EventEnabledMonoProfilerGCMoves ()) + return; + + // TODO: Needs to be async safe. + /*uint64_t obj_count = count / 2; + + GCObjectAddressData data [32]; + uint64_t data_chunks = obj_count / G_N_ELEMENTS (data); + uint64_t data_rest = obj_count % G_N_ELEMENTS (data); + uint64_t current_obj = 0; + + for (int chunk = 0; chunk < data_chunks; chunk++) { + for (int i = 0; i < G_N_ELEMENTS (data); i++) { + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj++]); + data [i].address = objects [current_obj++]; + } + + FireEtwMonoProfilerGCMoves ( + clr_instance_get_id (), + G_N_ELEMENTS (data), + sizeof (GCObjectAddressData), + data, + NULL, + NULL); + } + + if ((data_rest != 0)&& (data_rest % 2 == 0)) { + for (int i = 0; i < data_rest; i++) { + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj++]); + data [i].address = objects [current_obj++]; + } + + FireEtwMonoProfilerGCMoves ( + clr_instance_get_id (), + data_rest, + sizeof (GCObjectAddressData), + data, + NULL, + NULL); + }*/ } static void -profiler_jit_begin ( +mono_profiler_gc_resize ( MonoProfiler *prof, - MonoMethod *method) + uintptr_t size) { - ep_rt_mono_write_event_jit_start (method); + if (!EventEnabledMonoProfilerGCResize ()) + return; + + // TODO: Needs to be async safe. + /*FireEtwMonoProfilerGCResize ( + clr_instance_get_id (), + (uint64_t)size, + NULL, + NULL);*/ } static void -profiler_jit_failed ( +mono_profiler_gc_handle_created ( MonoProfiler *prof, - MonoMethod *method) + uint32_t handle, + MonoGCHandleType type, + MonoObject *object) { - //TODO: CoreCLR doesn't have this case, so no failure event currently exists. + if (!EventEnabledMonoProfilerGCHandleCreated ()) + return; + + FireEtwMonoProfilerGCHandleCreated ( + clr_instance_get_id (), + handle, + (uint8_t)type, + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_jit_done ( +mono_profiler_gc_handle_deleted ( MonoProfiler *prof, - MonoMethod *method, - MonoJitInfo *ji) + uint32_t handle, + MonoGCHandleType type) { - ep_rt_mono_write_event_method_load (method, ji); - ep_rt_mono_write_event_method_il_to_native_map (method, ji); + if (!EventEnabledMonoProfilerGCHandleDeleted ()) + return; + + FireEtwMonoProfilerGCHandleDeleted ( + clr_instance_get_id (), + handle, + (uint8_t)type, + NULL, + NULL); } static void -profiler_image_loaded ( - MonoProfiler *prof, - MonoImage *image) +mono_profiler_gc_finalizing (MonoProfiler *prof) { - if (image && image->heap_pdb.size == 0) - ep_rt_mono_write_event_module_load (image); + if (!EventEnabledMonoProfilerGCFinalizing ()) + return; + + FireEtwMonoProfilerGCFinalizing ( + clr_instance_get_id (), + NULL, + NULL); } static void -profiler_image_unloaded ( - MonoProfiler *prof, - MonoImage *image) +mono_profiler_gc_finalized (MonoProfiler *prof) { - if (image && image->heap_pdb.size == 0) - ep_rt_mono_write_event_module_unload (image); + if (!EventEnabledMonoProfilerGCFinalized ()) + return; + + FireEtwMonoProfilerGCFinalized ( + clr_instance_get_id (), + NULL, + NULL); } static void -profiler_assembly_loaded ( +mono_profiler_gc_finalizing_object ( MonoProfiler *prof, - MonoAssembly *assembly) + MonoObject *object) { - ep_rt_mono_write_event_assembly_load (assembly); + if (!EventEnabledMonoProfilerGCFinalizingObject ()) + return; + + FireEtwMonoProfilerGCFinalizingObject ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_assembly_unloaded ( +mono_profiler_gc_finalized_object ( MonoProfiler *prof, - MonoAssembly *assembly) + MonoObject * object) { - ep_rt_mono_write_event_assembly_unload (assembly); + if (!EventEnabledMonoProfilerGCFinalizedObject ()) + return; + + FireEtwMonoProfilerGCFinalizedObject ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_thread_started ( +mono_profiler_gc_root_register ( MonoProfiler *prof, - uintptr_t tid) + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name) { - ep_rt_mono_write_event_thread_created (ep_rt_uint64_t_to_thread_id_t (tid)); + if (!EventEnabledMonoProfilerGCRootRegister ()) + return; + + FireEtwMonoProfilerGCRootRegister ( + clr_instance_get_id (), + start, + (uint64_t)size, + (uint8_t) source, + (uint64_t)key, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); } static void -profiler_thread_stopped ( +mono_profiler_gc_root_unregister ( MonoProfiler *prof, - uintptr_t tid) + const mono_byte *start) { - ep_rt_mono_write_event_thread_terminated (ep_rt_uint64_t_to_thread_id_t (tid)); + if (!EventEnabledMonoProfilerGCRootUnregister ()) + return; + + FireEtwMonoProfilerGCRootUnregister ( + clr_instance_get_id (), + start, + NULL, + NULL); } static void -profiler_class_loading ( +mono_profiler_gc_roots ( MonoProfiler *prof, - MonoClass *klass) + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects) { - ep_rt_mono_write_event_type_load_start (m_class_get_byval_arg (klass)); + if (!EventEnabledMonoProfilerGCRoots ()) + return; + + // TODO: Needs to be async safe. + /*GCAddressObjectData data [32]; + uint64_t data_chunks = count / G_N_ELEMENTS (data); + uint64_t data_rest = count % G_N_ELEMENTS (data); + uint64_t current_obj = 0; + + for (int chunk = 0; chunk < data_chunks; chunk++) { + for (int i = 0; i < G_N_ELEMENTS (data); i++) { + data [i].address = addresses [current_obj]; + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj]); + current_obj++; + } + + FireEtwMonoProfilerGCRoots ( + clr_instance_get_id (), + G_N_ELEMENTS (data), + sizeof (GCAddressObjectData), + data, + NULL, + NULL); + } + + if (data_rest != 0) { + for (int i = 0; i < data_rest; i++) { + data [i].address = addresses [current_obj]; + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj]); + current_obj++; + } + + FireEtwMonoProfilerGCRoots ( + clr_instance_get_id (), + data_rest, + sizeof (GCAddressObjectData), + data, + NULL, + NULL); + }*/ } static void -profiler_class_failed ( +mono_profiler_monitor_contention ( MonoProfiler *prof, - MonoClass *klass) + MonoObject *object) { - ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); + if (!EventEnabledMonoProfilerMonitorContention ()) + return; + + FireEtwMonoProfilerMonitorContention ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_class_loaded ( +mono_profiler_monitor_failed ( MonoProfiler *prof, - MonoClass *klass) + MonoObject *object) { - ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); + if (!EventEnabledMonoProfilerMonitorFailed ()) + return; + + FireEtwMonoProfilerMonitorFailed ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_exception_throw ( +mono_profiler_monitor_acquired ( MonoProfiler *prof, - MonoObject *exc) + MonoObject *object) { - ep_rt_mono_write_event_exception_thrown (exc); + if (!EventEnabledMonoProfilerMonitorAcquired ()) + return; + + FireEtwMonoProfilerMonitorAcquired ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); } static void -profiler_exception_clause ( +mono_profiler_thread_started ( MonoProfiler *prof, - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *exc) + uintptr_t tid) { - ep_rt_mono_write_event_exception_clause (method, clause_num, clause_type, exc); + if (!EventEnabledMonoProfilerThreadStarted ()) + return; + + FireEtwMonoProfilerThreadStarted ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); } static void -profiler_monitor_contention ( +mono_profiler_thread_stopping ( MonoProfiler *prof, - MonoObject *obj) + uintptr_t tid) { - ep_rt_mono_write_event_monitor_contention_start (obj); + if (!EventEnabledMonoProfilerThreadStopping ()) + return; + + FireEtwMonoProfilerThreadStopping ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); } static void -profiler_monitor_acquired ( +mono_profiler_thread_stopped ( MonoProfiler *prof, - MonoObject *obj) + uintptr_t tid) { - ep_rt_mono_write_event_monitor_contention_stop (obj); + if (!EventEnabledMonoProfilerThreadStopped ()) + return; + + FireEtwMonoProfilerThreadStopped ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); } static void -profiler_monitor_failed ( +mono_profiler_thread_exited ( MonoProfiler *prof, - MonoObject *obj) + uintptr_t tid) { - ep_rt_mono_write_event_monitor_contention_stop (obj); + if (!EventEnabledMonoProfilerThreadExited ()) + return; + + FireEtwMonoProfilerThreadExited ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); } static void -profiler_jit_code_buffer ( +mono_profiler_thread_name ( MonoProfiler *prof, - const mono_byte *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data) + uintptr_t tid, + const char *name) { - ep_rt_mono_write_event_method_jit_memory_allocated_for_code ((const uint8_t *)buffer, size, type, data); + if (!EventEnabledMonoProfilerThreadName ()) + return; + + FireEtwMonoProfilerThreadName ( + clr_instance_get_id (), + (uint64_t)tid, + (ep_char8_t *)(name ? name : ""), + NULL, + NULL); } void -EventPipeEtwCallbackDotNETRuntime ( +EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( const uint8_t *source_id, unsigned long is_enabled, uint8_t level, @@ -2992,55 +4907,261 @@ EventPipeEtwCallbackDotNETRuntime ( ep_rt_config_requires_lock_not_held (); EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; - EP_ASSERT (_ep_rt_mono_profiler != NULL); + EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); + + match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; EP_LOCK_ENTER (section1) - if (is_enabled == 1 && !MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled) { - // Add profiler callbacks for DotNETRuntime provider events. - mono_profiler_set_jit_begin_callback (_ep_rt_mono_profiler, profiler_jit_begin); - mono_profiler_set_jit_failed_callback (_ep_rt_mono_profiler, profiler_jit_failed); - mono_profiler_set_jit_done_callback (_ep_rt_mono_profiler, profiler_jit_done); - mono_profiler_set_image_loaded_callback (_ep_rt_mono_profiler, profiler_image_loaded); - mono_profiler_set_image_unloaded_callback (_ep_rt_mono_profiler, profiler_image_unloaded); - mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_profiler, profiler_assembly_loaded); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_profiler, profiler_assembly_unloaded); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, profiler_thread_started); - mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_thread_stopped); - mono_profiler_set_class_loading_callback (_ep_rt_mono_profiler, profiler_class_loading); - mono_profiler_set_class_failed_callback (_ep_rt_mono_profiler, profiler_class_failed); - mono_profiler_set_class_loaded_callback (_ep_rt_mono_profiler, profiler_class_loaded); - mono_profiler_set_exception_throw_callback (_ep_rt_mono_profiler, profiler_exception_throw); - mono_profiler_set_exception_clause_callback (_ep_rt_mono_profiler, profiler_exception_clause); - mono_profiler_set_monitor_contention_callback (_ep_rt_mono_profiler, profiler_monitor_contention); - mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_profiler, profiler_monitor_acquired); - mono_profiler_set_monitor_failed_callback (_ep_rt_mono_profiler, profiler_monitor_failed); - mono_profiler_set_jit_code_buffer_callback (_ep_rt_mono_profiler, profiler_jit_code_buffer); - } else if (is_enabled == 0 && MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled) { - // Remove profiler callbacks for DotNETRuntime provider events. - mono_profiler_set_jit_code_buffer_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_contention_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_exception_clause_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_exception_throw_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_loading_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_image_unloaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_image_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_done_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_begin_callback (_ep_rt_mono_profiler, NULL); + uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; + + if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loading); + mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loaded); + mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloading); + mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloaded); + mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_name); + mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loading); + mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_failed); + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loaded); + mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloading); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloaded); + mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loading); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loaded); + mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloading); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_begin); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_failed); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_done); + mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_created); + mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_destroyed); + mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_code_buffer); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, TYPE_LOADING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loading); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_failed); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loaded); + mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loading); + mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_failed); + mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, METHOD_TRACING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { + mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_enter); + mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_leave); + mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_tail_call); + mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_exception_leave); + mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_free); + mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_begin_invoke); + mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_end_invoke); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { + mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_throw); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_clause); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) { + mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_event); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) { + mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_allocation); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_moves); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_resize); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_created); + mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_deleted); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing); + mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized); + mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing_object); + mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized_object); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_register); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_unregister); + mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_roots); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_contention); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_failed); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_acquired); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, THREADING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_started); + mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopping); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopped); + mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_exited); + mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_name); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (!_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { + if (profiler_callback_is_enabled(match_any_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } } - EP_LOCK_EXIT (section1) - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + EP_LOCK_EXIT (section1) ep_on_exit: ep_rt_config_requires_lock_not_held (); @@ -3050,51 +5171,6 @@ EventPipeEtwCallbackDotNETRuntime ( ep_exit_error_handler (); } -void -EventPipeEtwCallbackDotNETRuntimeRundown ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - -void -EventPipeEtwCallbackDotNETRuntimePrivate ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - -void -EventPipeEtwCallbackDotNETRuntimeStress ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - #endif /* ENABLE_PERFTRACING */ MONO_EMPTY_SOURCE_FILE(eventpipe_rt_mono); diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 2c3a5f7e36d70..49b6456cdb9df 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -2344,6 +2344,16 @@ EventPipeEtwCallbackDotNETRuntimeStress ( EventFilterDescriptor *filter_data, void *callback_data); +void +EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data); + #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_RT_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst index f74d476889e65..ad52dd9f8c8eb 100644 --- a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst +++ b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst @@ -45,3 +45,4 @@ ThreadPoolWorkingThreadCount ThreadTerminated TypeLoadStart TypeLoadStop +Microsoft-DotNETRuntimeMonoProfiler:* \ No newline at end of file From b7ce14c329dabbbdf5f25349fb7b4665042774cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 9 Jul 2021 17:55:31 -0400 Subject: [PATCH 40/72] Disallow TypedReference and byref types in MakeArrayType (#55403) Fixes https://github.com/dotnet/runtime/issues/39001 --- .../tests/System/Type/TypeTests.cs | 1 - src/mono/mono/metadata/icall.c | 20 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs index ca984b4331865..f8816f13a62ab 100644 --- a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs +++ b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs @@ -305,7 +305,6 @@ public static IEnumerable MakeArrayType_ByRef_TestData() [Theory] [MemberData(nameof(MakeArrayType_ByRef_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39001", TestRuntimes.Mono)] public void MakeArrayType_ByRef_ThrowsTypeLoadException(Type t) { Assert.Throws(() => t.MakeArrayType()); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index d725b1c3b5fd1..7c3f33fc79219 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6283,15 +6283,25 @@ ves_icall_System_Reflection_RuntimeModule_ResolveSignature (MonoImage *image, gu } static void -check_for_invalid_array_type (MonoClass *klass, MonoError *error) +check_for_invalid_array_type (MonoType *type, MonoError *error) { + gboolean allowed = TRUE; char *name; error_init (error); - if (m_class_get_byval_arg (klass)->type != MONO_TYPE_TYPEDBYREF) - return; + if (type->byref) + allowed = FALSE; + else if (type->type == MONO_TYPE_TYPEDBYREF) + allowed = FALSE; + + MonoClass *klass = mono_class_from_mono_type_internal (type); + if (m_class_is_byreflike (klass)) + allowed = FALSE; + + if (allowed) + return; name = mono_type_get_full_name (klass); mono_error_set_type_load_name (error, name, g_strdup (""), ""); } @@ -6307,9 +6317,9 @@ ves_icall_RuntimeType_make_array_type (MonoReflectionTypeHandle ref_type, int ra { MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); - MonoClass *klass = mono_class_from_mono_type_internal (type); - check_for_invalid_array_type (klass, error); + check_for_invalid_array_type (type, error); return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE)); + MonoClass *klass = mono_class_from_mono_type_internal (type); MonoClass *aklass; if (rank == 0) //single dimension array From c90aa4d8ab36bd454e5937abd903924fe7e859fe Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 01:05:11 +0300 Subject: [PATCH 41/72] [interp] Add a few fixes for conversion opcodes (#55418) * [interp] Add missing conversion from r4 to uint * [interp] Remove duplicated and inconsistent code for conv.ovf.i.un and conv.ovf.u.un Just use the existing path for the i4/i8/u4/u8 opcodes. --- src/mono/mono/mini/interp/transform.c | 45 +++++++++------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 4a17c9410b724..0eed6c7146e01 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -5308,6 +5308,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U4_R8); #else interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U8_R8); +#endif + break; + case STACK_TYPE_R4: +#if SIZEOF_VOID_P == 4 + interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U4_R4); +#else + interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U8_R4); #endif break; case STACK_TYPE_I4: @@ -6133,36 +6140,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; break; } +#if SIZEOF_VOID_P == 8 case CEE_CONV_OVF_I_UN: case CEE_CONV_OVF_U_UN: - CHECK_STACK (td, 1); - switch (td->sp [-1].type) { - case STACK_TYPE_R8: -#if SIZEOF_VOID_P == 8 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I8_UN_R8); -#else - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_UN_R8); -#endif - break; - case STACK_TYPE_I8: -#if SIZEOF_VOID_P == 4 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_U8); #endif - break; - case STACK_TYPE_I4: -#if SIZEOF_VOID_P == 8 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_I8_U4); -#elif SIZEOF_VOID_P == 4 - if (*td->ip == CEE_CONV_OVF_I_UN) - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_U4); -#endif - break; - default: - g_assert_not_reached (); - break; - } - ++td->ip; - break; case CEE_CONV_OVF_I8_UN: case CEE_CONV_OVF_U8_UN: CHECK_STACK (td, 1); @@ -6171,7 +6152,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_OVF_I8_UN_R8); break; case STACK_TYPE_I8: - if (*td->ip == CEE_CONV_OVF_I8_UN) + if (*td->ip == CEE_CONV_OVF_I8_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_OVF_I8_U8); break; case STACK_TYPE_I4: @@ -6619,6 +6600,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_I: + case CEE_CONV_OVF_I_UN: #endif case CEE_CONV_OVF_I4: case CEE_CONV_OVF_I4_UN: @@ -6631,11 +6613,11 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_R8); break; case STACK_TYPE_I4: - if (*td->ip == CEE_CONV_OVF_I4_UN) + if (*td->ip == CEE_CONV_OVF_I4_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_U4); break; case STACK_TYPE_I8: - if (*td->ip == CEE_CONV_OVF_I4_UN) + if (*td->ip == CEE_CONV_OVF_I4_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_U8); else interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_I8); @@ -6647,6 +6629,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_U: + case CEE_CONV_OVF_U_UN: #endif case CEE_CONV_OVF_U4: case CEE_CONV_OVF_U4_UN: @@ -6659,7 +6642,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_U4_R8); break; case STACK_TYPE_I4: - if (*td->ip != CEE_CONV_OVF_U4_UN) + if (*td->ip == CEE_CONV_OVF_U4 || *td->ip == CEE_CONV_OVF_U) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_U4_I4); break; case STACK_TYPE_I8: From fe9005ff523faa681448e3e5ea67f48b3352e3da Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 19:47:40 -0400 Subject: [PATCH 42/72] Use RandomAccess/File.OpenHandle in a few more places (#55150) Places where we don't need to pay for the FileStream and the access pattern is easily replaced. --- .../src/System/IO/File.cs | 36 +++++++------------ .../src/System/IO/FileSystem.Unix.cs | 7 ++-- .../src/System/IO/RandomAccess.Windows.cs | 4 +-- .../src/System/TimeZoneInfo.Unix.cs | 8 +++-- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 8480e46a094d3..1f24874266c45 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -10,6 +10,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; #if MS_IO_REDIST using System; @@ -373,19 +374,13 @@ public static void WriteAllBytes(string path, byte[] bytes) if (bytes == null) throw new ArgumentNullException(nameof(bytes)); - InternalWriteAllBytes(path, bytes); - } - - private static void InternalWriteAllBytes(string path, byte[] bytes) - { - Debug.Assert(path != null); - Debug.Assert(path.Length != 0); - Debug.Assert(bytes != null); - - using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - fs.Write(bytes, 0, bytes.Length); - } +#if MS_IO_REDIST + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); + fs.Write(bytes, 0, bytes.Length); +#else + using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read); + RandomAccess.WriteAtOffset(sfh, bytes, 0); +#endif } public static string[] ReadAllLines(string path) { @@ -836,22 +831,17 @@ private static async Task InternalReadAllBytesUnknownLengthAsync(FileStr return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) - : InternalWriteAllBytesAsync(path, bytes, cancellationToken); - } - - private static async Task InternalWriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken) - { - Debug.Assert(!string.IsNullOrEmpty(path)); - Debug.Assert(bytes != null); + : Core(path, bytes, cancellationToken); - using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan)) + static async Task Core(string path, byte[] bytes, CancellationToken cancellationToken) { #if MS_IO_REDIST + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 1, FileOptions.Asynchronous | FileOptions.SequentialScan); await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); #else - await fs.WriteAsync(new ReadOnlyMemory(bytes), cancellationToken).ConfigureAwait(false); + using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous | FileOptions.SequentialScan); + await RandomAccess.WriteAtOffsetAsync(sfh, bytes, 0, cancellationToken).ConfigureAwait(false); #endif - await fs.FlushAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 0a4ef7cdef5fb..c80e585eda865 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using Microsoft.Win32.SafeHandles; namespace System.IO { @@ -25,10 +26,10 @@ public static void CopyFile(string sourceFullPath, string destFullPath, bool ove } // Copy the contents of the file from the source to the destination, creating the destination in the process - using (var src = new FileStream(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.None)) - using (var dst = new FileStream(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, DefaultBufferSize, FileOptions.None)) + using (SafeFileHandle src = File.OpenHandle(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.None)) + using (SafeFileHandle dst = File.OpenHandle(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.None)) { - Interop.CheckIo(Interop.Sys.CopyFile(src.SafeFileHandle, dst.SafeFileHandle)); + Interop.CheckIo(Interop.Sys.CopyFile(src, dst)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 20dcbdeac8e77..63639cc8503a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -208,7 +208,7 @@ private static unsafe int WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadO } } - private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) => handle.IsAsync ? Map(QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken)) : ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); @@ -269,7 +269,7 @@ internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int error return (vts, -1); } - private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) => handle.IsAsync ? Map(QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken)) : ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index ebc626a622194..1356e65758c5c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -11,6 +11,7 @@ using System.Text; using System.Threading; using System.Security; +using Microsoft.Win32.SafeHandles; namespace System { @@ -615,16 +616,17 @@ private static bool CompareTimeZoneFile(string filePath, byte[] buffer, byte[] r try { // bufferSize == 1 used to avoid unnecessary buffer in FileStream - using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1)) + using (SafeFileHandle sfh = File.OpenHandle(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == rawData.Length) + long fileLength = RandomAccess.GetLength(sfh); + if (fileLength == rawData.Length) { int index = 0; int count = rawData.Length; while (count > 0) { - int n = stream.Read(buffer, index, count); + int n = RandomAccess.Read(sfh, buffer.AsSpan(index, count), index); if (n == 0) ThrowHelper.ThrowEndOfFileException(); From e0b9fe5146741af0c0bab434cbece4f67a3a677e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 19:56:02 -0400 Subject: [PATCH 43/72] Fix File.Copy with procfs (#55358) --- .../Native/Unix/System.Native/pal_io.c | 41 +++++++++++-------- .../System.IO.FileSystem/tests/File/Copy.cs | 12 ++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 59b8f2770c103..6534265085639 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -1208,41 +1208,46 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) return -1; } - // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. uint64_t size = (uint64_t)sourceStat.st_size; - - // Note that per man page for large files, you have to iterate until the - // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). - while (size > 0) + if (size != 0) { - ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); - if (sent < 0) + // Note that per man page for large files, you have to iterate until the + // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). + while (size > 0) { - if (errno != EINVAL && errno != ENOSYS) + ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); + if (sent < 0) { - return -1; + if (errno != EINVAL && errno != ENOSYS) + { + return -1; + } + else + { + break; + } } else { - break; + assert((size_t)sent <= size); + size -= (size_t)sent; } } - else + + if (size == 0) { - assert((size_t)sent <= size); - size -= (size_t)sent; + copied = true; } } - if (size == 0) - { - copied = true; - } + // sendfile couldn't be used; fall back to a manual copy below. This could happen // if we're on an old kernel, for example, where sendfile could only be used - // with sockets and not regular files. + // with sockets and not regular files. Additionally, certain files (e.g. procfs) + // may return a size of 0 even though reading from then will produce data. As such, + // we avoid using sendfile with the queried size if the size is reported as 0. #endif // HAVE_SENDFILE_4 // Manually read all data from the source and write it to the destination. diff --git a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs index 0654f99dc9ce3..eb2686807bc1c 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs @@ -239,6 +239,18 @@ public void WindowsAlternateDataStream(string defaultStream, string alternateStr Assert.Throws(() => Copy(testFileAlternateStream, testFile2)); Assert.Throws(() => Copy(testFileAlternateStream, testFile2 + alternateStream)); } + + [Theory] + [PlatformSpecific(TestPlatforms.Linux)] + [InlineData("/proc/cmdline")] + [InlineData("/proc/version")] + [InlineData("/proc/filesystems")] + public void Linux_CopyFromProcfsToFile(string path) + { + string testFile = GetTestFilePath(); + File.Copy(path, testFile); + Assert.Equal(File.ReadAllText(path), File.ReadAllText(testFile)); // assumes chosen files won't change between reads + } #endregion } From 0d848f68b46cd34bca7f64b11b79057f4b7bfad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 10 Jul 2021 01:56:54 +0200 Subject: [PATCH 44/72] Add possibility to write ANSI color escape codes when the console output is redirected (#47935) * Add possibility to write ANSI color escape codes when the console output is redirected This introduces an opt-in mechanism to allow writing ANSI color escape codes even when the console output is redirected. To enable ANSI color codes, the `DOTNET_CONSOLE_ANSI_COLOR` environment variable must be set to `true` (case insensitive) or `1`. Fixes #33980 * Fix ANSI color env var handling Co-authored-by: Stephen Toub --- .../src/System/ConsolePal.Unix.cs | 50 +++++++++++++++- src/libraries/System.Console/tests/Color.cs | 60 +++++++++++++++++-- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index eacf15a203efd..5f2e485aca5e4 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -39,6 +39,9 @@ internal static class ConsolePal private static int s_windowHeight; // Cached WindowHeight, invalid when s_windowWidth == -1. private static int s_invalidateCachedSettings = 1; // Tracks whether we should invalidate the cached settings. + /// Whether to output ansi color strings. + private static volatile int s_emitAnsiColorCodes = -1; + public static Stream OpenStandardInput() { return new UnixConsoleStream(SafeFileHandleHelper.Open(() => Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDIN_FILENO)), FileAccess.Read, @@ -779,8 +782,10 @@ private static void WriteSetColorString(bool foreground, ConsoleColor color) // Changing the color involves writing an ANSI character sequence out to the output stream. // We only want to do this if we know that sequence will be interpreted by the output. // rather than simply displayed visibly. - if (Console.IsOutputRedirected) + if (!EmitAnsiColorCodes) + { return; + } // See if we've already cached a format string for this foreground/background // and specific color choice. If we have, just output that format string again. @@ -813,13 +818,52 @@ private static void WriteSetColorString(bool foreground, ConsoleColor color) /// Writes out the ANSI string to reset colors. private static void WriteResetColorString() { - // We only want to send the reset string if we're targeting a TTY device - if (!Console.IsOutputRedirected) + if (EmitAnsiColorCodes) { WriteStdoutAnsiString(TerminalFormatStrings.Instance.Reset); } } + /// Get whether to emit ANSI color codes. + private static bool EmitAnsiColorCodes + { + get + { + // The flag starts at -1. If it's no longer -1, it's 0 or 1 to represent false or true. + int emitAnsiColorCodes = s_emitAnsiColorCodes; + if (emitAnsiColorCodes != -1) + { + return Convert.ToBoolean(emitAnsiColorCodes); + } + + // We've not yet computed whether to emit codes or not. Do so now. We may race with + // other threads, and that's ok; this is idempotent unless someone is currently changing + // the value of the relevant environment variables, in which case behavior here is undefined. + + // By default, we emit ANSI color codes if output isn't redirected, and suppress them if output is redirected. + bool enabled = !Console.IsOutputRedirected; + + if (enabled) + { + // We subscribe to the informal standard from https://no-color.org/. If we'd otherwise emit + // ANSI color codes but the NO_COLOR environment variable is set, disable emitting them. + enabled = Environment.GetEnvironmentVariable("NO_COLOR") is null; + } + else + { + // We also support overriding in the other direction. If we'd otherwise avoid emitting color + // codes but the DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION environment variable is + // set to 1 or true, enable color. + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"); + enabled = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); + } + + // Store and return the computed answer. + s_emitAnsiColorCodes = Convert.ToInt32(enabled); + return enabled; + } + } + /// /// The values of the ConsoleColor enums unfortunately don't map to the /// corresponding ANSI values. We need to do the mapping manually. diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 567faad01ca4c..55843aa2f3724 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -2,15 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using System.Text; -using Microsoft.DotNet.XUnitExtensions; +using Microsoft.DotNet.RemoteExecutor; using Xunit; public class Color { + private const char Esc = (char)0x1B; + [Fact] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] public static void InvalidColors() @@ -64,9 +67,58 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() Console.ResetColor(); Console.Write('4'); - const char Esc = (char)0x1B; Assert.Equal(0, Encoding.UTF8.GetString(data.ToArray()).ToCharArray().Count(c => c == Esc)); Assert.Equal("1234", Encoding.UTF8.GetString(data.ToArray())); }); } + + public static bool TermIsSet => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TERM")); + + [ConditionalTheory(nameof(TermIsSet))] + [PlatformSpecific(TestPlatforms.AnyUnix)] + [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] + [InlineData(null)] + [InlineData("1")] + [InlineData("true")] + [InlineData("tRuE")] + [InlineData("0")] + [InlineData("false")] + public static void RedirectedOutput_EnvVarSet_EmitsAnsiCodes(string envVar) + { + var psi = new ProcessStartInfo { RedirectStandardOutput = true }; + psi.Environment["DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"] = envVar; + + for (int i = 0; i < 3; i++) + { + Action main = i => + { + Console.Write("SEPARATOR"); + switch (i) + { + case "0": + Console.ForegroundColor = ConsoleColor.Blue; + break; + + case "1": + Console.BackgroundColor = ConsoleColor.Red; + break; + + case "2": + Console.ResetColor(); + break; + } + Console.Write("SEPARATOR"); + }; + + using RemoteInvokeHandle remote = RemoteExecutor.Invoke(main, i.ToString(CultureInfo.InvariantCulture), new RemoteInvokeOptions() { StartInfo = psi }); + + bool expectedEscapes = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); + + string stdout = remote.Process.StandardOutput.ReadToEnd(); + string[] parts = stdout.Split("SEPARATOR"); + Assert.Equal(3, parts.Length); + + Assert.Equal(expectedEscapes, parts[1].Contains(Esc)); + } + } } From 3b8b527ef638c651e2d41299426bf91b93563196 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Sat, 10 Jul 2021 02:00:20 +0200 Subject: [PATCH 45/72] Big-endian fix: ApplyMask in System.Net.WebSockets.ManagedWebSocket (#55422) * Fix mask shifting logic for big-endian systems --- .../src/System/Net/WebSockets/ManagedWebSocket.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index ac0fad583d9bc..864b742561796 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -1503,7 +1503,15 @@ private static unsafe int ApplyMask(Span toMask, int mask, int maskIndex) maskIndex = (maskIndex + 1) & 3; } - int rolledMask = (int)BitOperations.RotateRight((uint)mask, maskIndex * 8); + int rolledMask; + if (BitConverter.IsLittleEndian) + { + rolledMask = (int)BitOperations.RotateRight((uint)mask, maskIndex * 8); + } + else + { + rolledMask = (int)BitOperations.RotateLeft((uint)mask, maskIndex * 8); + } // use SIMD if possible. From bea43044f8fbf78e77ca3d1085ae0f6bdc05ead9 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 9 Jul 2021 20:01:05 -0400 Subject: [PATCH 46/72] Fix one-shot CBC documentation (#55432) * Fix one-shot CBC documentation. The CBC one-shot documentation made a few references to the ECB one shots. This removes them and uses consistent language that is used on the other one-shot methods. * Change ECB to be consistent --- .../src/System/Security/Cryptography/SymmetricAlgorithm.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs index 857edc6d03c54..f5485577c0707 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs @@ -561,7 +561,7 @@ public bool TryDecryptEcb(ReadOnlySpan ciphertext, Span destination, /// is not a valid padding mode. /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . @@ -842,7 +842,7 @@ public bool TryDecryptCbc( /// that is exactly in length, converted to bytes (BlockSize / 8). /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . @@ -872,7 +872,7 @@ public byte[] EncryptCbc(byte[] plaintext, byte[] iv, PaddingMode paddingMode = /// that is exactly in length, converted to bytes (BlockSize / 8). /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . From a54e25b67494bd4ff49a650566d74984b7facdfb Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Sat, 10 Jul 2021 02:02:38 +0200 Subject: [PATCH 47/72] Nullable annotations for the remainder of System.Data.Common (#55409) --- .../System.ComponentModel.TypeConverter.cs | 4 +- .../ComponentModel/CustomTypeDescriptor.cs | 2 +- .../ComponentModel/ICustomTypeDescriptor.cs | 2 +- .../ReflectTypeDescriptionProvider.cs | 4 +- .../System/ComponentModel/TypeDescriptor.cs | 8 +- .../ref/System.Data.Common.cs | 6 +- .../System/Data/Common/AdapterUtil.Common.cs | 19 +- .../System/Data/Common/DataRecordInternal.cs | 2 +- .../src/System/Data/Common/DataStorage.cs | 2 +- .../Data/Common/DbConnectionStringBuilder.cs | 2 +- .../src/System/Data/Common/DbDataRecord.cs | 2 +- .../src/System/Data/Common/ObjectStorage.cs | 2 +- .../src/System/Data/Common/SqlUDTStorage.cs | 2 +- .../src/System/Data/DataColumn.cs | 2 +- .../src/System/Data/DataException.cs | 2 +- .../src/System/Data/DataRowView.cs | 2 +- .../src/System/Data/DataTableCollection.cs | 2 +- .../DataViewManagerListItemTypeDescriptor.cs | 27 +- .../src/System/Data/SimpleType.cs | 4 +- .../src/System/Data/XMLDiffLoader.cs | 79 ++-- .../src/System/Data/XMLSchema.cs | 445 +++++++++--------- .../src/System/Data/XmlDataLoader.cs | 193 ++++---- .../src/System/Data/XmlToDatasetMap.cs | 87 ++-- 23 files changed, 442 insertions(+), 458 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index e222d17e5aa31..6a3d62e34a7b0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -303,7 +303,7 @@ protected CustomTypeDescriptor(System.ComponentModel.ICustomTypeDescriptor? pare public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes) { throw null; } - public virtual object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + public virtual object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DataObjectAttribute : System.Attribute @@ -585,7 +585,7 @@ public partial interface ICustomTypeDescriptor System.ComponentModel.PropertyDescriptorCollection GetProperties(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes); - object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd); + object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd); } public partial interface IDataErrorInfo { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs index 17923164ab301..7adad3b3bc0dc 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs @@ -179,6 +179,6 @@ public virtual PropertyDescriptorCollection GetProperties(Attribute[]? attribute /// returned. Returning null from this method causes the TypeDescriptor object /// to use its default type description services. /// - public virtual object? GetPropertyOwner(PropertyDescriptor pd) => _parent?.GetPropertyOwner(pd); + public virtual object? GetPropertyOwner(PropertyDescriptor? pd) => _parent?.GetPropertyOwner(pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs index fd9b681c20e8a..57b602ac9e963 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs @@ -77,6 +77,6 @@ public interface ICustomTypeDescriptor /// /// Gets the object that directly depends on this value being edited. /// - object? GetPropertyOwner(PropertyDescriptor pd); + object? GetPropertyOwner(PropertyDescriptor? pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 9908aa66c330a..f41b9fca2a130 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -791,7 +791,7 @@ private static IExtenderProvider[] GetExtenders(ICollection components, object i /// /// Retrieves the owner for a property. /// - internal object GetExtendedPropertyOwner(object instance, PropertyDescriptor pd) + internal object GetExtendedPropertyOwner(object instance, PropertyDescriptor? pd) { return GetPropertyOwner(instance.GetType(), instance, pd); } @@ -873,7 +873,7 @@ internal PropertyDescriptorCollection GetProperties([DynamicallyAccessedMembers( /// /// Retrieves the owner for a property. /// - internal object GetPropertyOwner(Type type, object instance, PropertyDescriptor pd) + internal object GetPropertyOwner(Type type, object instance, PropertyDescriptor? pd) { return TypeDescriptor.GetAssociation(type, instance); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index d8fae944f83c7..72f1dadd00c57 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -2771,7 +2771,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? at return _handler.GetProperties(_instance, attributes); } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => _instance; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => _instance; } } @@ -3121,7 +3121,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? at /// /// ICustomTypeDescriptor implementation. /// - object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return _primary.GetPropertyOwner(pd) ?? _secondary.GetPropertyOwner(pd); } @@ -3570,7 +3570,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? at /// ICustomTypeDescriptor implementation. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3915,7 +3915,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? at /// /// ICustomTypeDescriptor implementation. /// - object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index 10041da15cefc..ca955720c0df1 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -481,7 +481,7 @@ public void EndEdit() { } System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } [System.ComponentModel.DefaultPropertyAttribute("DataSetName")] [System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.Data.VS.DataSetDesigner, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] @@ -2207,7 +2207,7 @@ void System.Collections.IDictionary.Remove(object keyword) { } System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } public override string ToString() { throw null; } public virtual bool TryGetValue(string keyword, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out object? value) { throw null; } } @@ -2398,7 +2398,7 @@ protected DbDataRecord() { } System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } public abstract partial class DbDataSourceEnumerator { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs index 38695b358c178..3bf38b5b09a0e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO-NULLABLE: Enable nullability as part of annotation System.Data.{Odbc,OleDb} -#nullable disable - using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -266,7 +263,7 @@ internal static ArgumentOutOfRangeException NotSupportedStatementType(StatementT // // DbConnectionOptions, DataAccess // - internal static ArgumentException InvalidKeyname(string parameterName) + internal static ArgumentException InvalidKeyname(string? parameterName) { return Argument(SR.ADP_InvalidKey, parameterName); } @@ -286,7 +283,7 @@ internal static Exception WrongType(Type got, Type expected) // // Generic Data Provider Collection // - internal static Exception CollectionUniqueValue(Type itemType, string propertyName, string propertyValue) + internal static Exception CollectionUniqueValue(Type itemType, string propertyName, string? propertyValue) { return Argument(SR.Format(SR.ADP_CollectionUniqueValue, itemType.Name, propertyName, propertyValue)); } @@ -306,19 +303,19 @@ private static InvalidOperationException DataMapping(string error) } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaExpression(string srcColumn, string cacheColumn) + internal static InvalidOperationException ColumnSchemaExpression(string? srcColumn, string cacheColumn) { return DataMapping(SR.Format(SR.ADP_ColumnSchemaExpression, srcColumn, cacheColumn)); } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaMismatch(string srcColumn, Type srcType, DataColumn column) + internal static InvalidOperationException ColumnSchemaMismatch(string? srcColumn, Type srcType, DataColumn column) { return DataMapping(SR.Format(SR.ADP_ColumnSchemaMismatch, srcColumn, srcType.Name, column.ColumnName, column.DataType.Name)); } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaMissing(string cacheColumn, string tableName, string srcColumn) + internal static InvalidOperationException ColumnSchemaMissing(string cacheColumn, string tableName, string? srcColumn) { if (string.IsNullOrEmpty(tableName)) { @@ -382,7 +379,7 @@ internal static Exception ColumnsIsParent(ICollection collection) { return ParametersIsParent(typeof(DataColumnMapping), collection); } - internal static Exception ColumnsUniqueSourceColumn(string srcColumn) + internal static Exception ColumnsUniqueSourceColumn(string? srcColumn) { return CollectionUniqueValue(typeof(DataColumnMapping), ADP.SourceColumn, srcColumn); } @@ -422,7 +419,7 @@ internal static Exception TablesSourceIndex(string srcTable) { return CollectionIndexString(typeof(DataTableMapping), ADP.SourceTable, srcTable, typeof(DataTableMappingCollection)); } - internal static Exception TablesUniqueSourceTable(string srcTable) + internal static Exception TablesUniqueSourceTable(string? srcTable) { return CollectionUniqueValue(typeof(DataTableMapping), ADP.SourceTable, srcTable); } @@ -840,6 +837,6 @@ private static int GenerateUniqueName(Dictionary hash, ref string c return uniqueIndex; } - internal static int SrcCompare(string strA, string strB) => strA == strB ? 0 : 1; + internal static int SrcCompare(string? strA, string? strB) => strA == strB ? 0 : 1; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs index c49d8d33ef0ca..ba0a44032fc69 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs @@ -368,7 +368,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? at return _propertyDescriptors; } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return this; } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs index 1a8b361c17f7d..8c28cc2210da2 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs @@ -274,7 +274,7 @@ public virtual void SetCapacity(int capacity) public abstract object ConvertXmlToObject(string s); [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { return ConvertXmlToObject(xmlReader.Value); } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 10567391a3ec5..27ffe33faa3b4 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -637,7 +637,7 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attribute { return TypeDescriptor.GetEvents(this, attributes, true); } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return this; } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs index f3e1827f8859e..14f3d84387724 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs @@ -101,6 +101,6 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => new PropertyDescriptorCollection(null); - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs index aa06791989d6e..7489652d9ba15 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs @@ -349,7 +349,7 @@ public override object ConvertXmlToObject(string s) // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { object? retValue = null; bool isBaseCLRType = false; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs index 16aea8a6c0bb2..b804c470ed02e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs @@ -173,7 +173,7 @@ public override object ConvertXmlToObject(string s) // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { if (null == xmlAttrib) { diff --git a/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs b/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs index 796ea59699d03..5d98ebf62ef9d 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs @@ -1798,7 +1798,7 @@ internal object ConvertXmlToObject(string s) } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { return InsureStorage().ConvertXmlToObject(xmlReader, xmlAttrib); } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataException.cs b/src/libraries/System.Data.Common/src/System/Data/DataException.cs index dfc6806e2efdc..301075bf54304 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataException.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataException.cs @@ -677,7 +677,7 @@ public static Exception RemovePrimaryKey(DataTable table) => table.TableName.Len public static Exception DiffgramMissingSQL() => _Data(SR.Xml_MissingSQL); public static Exception DuplicateConstraintRead(string str) => _Data(SR.Format(SR.Xml_DuplicateConstraint, str)); public static Exception ColumnTypeConflict(string name) => _Data(SR.Format(SR.Xml_ColumnConflict, name)); - public static Exception CannotConvert(string name, string type) => _Data(SR.Format(SR.Xml_CannotConvert, name, type)); + public static Exception CannotConvert(string name, string? type) => _Data(SR.Format(SR.Xml_CannotConvert, name, type)); public static Exception MissingRefer(string name) => _Data(SR.Format(SR.Xml_MissingRefer, Keywords.REFER, Keywords.XSD_KEYREF, name)); public static Exception InvalidPrefix(string name) => _Data(SR.Format(SR.Xml_InvalidPrefix_SpecialCharacters, name)); public static Exception CanNotDeserializeObjectType() => _InvalidOperation(SR.Xml_CanNotDeserializeObjectType); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs index dc0dd2e7aad7e..c12d20bfd034c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs @@ -257,7 +257,7 @@ public void EndEdit() PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => (_dataView.Table != null ? _dataView.Table.GetPropertyDescriptorCollection(attributes) : s_zeroPropertyDescriptorCollection); - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; #endregion } } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs b/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs index 5677da8639e5d..e4dd696086956 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs @@ -119,7 +119,7 @@ public DataTable? this[string? name] // Case-sensitive smart search: it will look for a table using the ns only if required to // resolve a conflict - internal DataTable? GetTableSmart(string name, string ns) + internal DataTable? GetTableSmart(string name, string? ns) { int fCount = 0; DataTable? fTable = null; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs index 20b61fc1a3651..d991f29e5f1e1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable - using System.ComponentModel; using System.Diagnostics.CodeAnalysis; @@ -12,7 +9,7 @@ namespace System.Data internal sealed class DataViewManagerListItemTypeDescriptor : ICustomTypeDescriptor { private readonly DataViewManager _dataViewManager; - private PropertyDescriptorCollection _propsCollection; + private PropertyDescriptorCollection? _propsCollection; internal DataViewManagerListItemTypeDescriptor(DataViewManager dataViewManager) { @@ -40,37 +37,37 @@ internal DataView GetDataView(DataTable table) /// Retrieves the class name for this object. If null is returned, /// the type name is used. /// - string ICustomTypeDescriptor.GetClassName() => null; + string? ICustomTypeDescriptor.GetClassName() => null; /// /// Retrieves the name for this object. If null is returned, /// the default is used. /// - string ICustomTypeDescriptor.GetComponentName() => null; + string? ICustomTypeDescriptor.GetComponentName() => null; /// /// Retrieves the type converter for this object. /// [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] - TypeConverter ICustomTypeDescriptor.GetConverter() => null; + TypeConverter ICustomTypeDescriptor.GetConverter() => null!; /// /// Retrieves the default event. /// [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() => null; /// /// Retrieves the default property. /// [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() => null; /// /// Retrieves the an editor for this object. /// [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; /// /// Retrieves an array of events that the given component instance @@ -88,7 +85,7 @@ internal DataView GetDataView(DataTable table) /// filtered by the given set of attributes. /// [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) => new EventDescriptorCollection(null); /// @@ -109,15 +106,15 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => /// filtered by the given set of attributes. /// [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => GetPropertiesInternal(); internal PropertyDescriptorCollection GetPropertiesInternal() { if (_propsCollection == null) { - PropertyDescriptor[] props = null; - DataSet dataSet = _dataViewManager.DataSet; + PropertyDescriptor[]? props = null; + DataSet? dataSet = _dataViewManager.DataSet; if (dataSet != null) { int tableCount = dataSet.Tables.Count; @@ -139,6 +136,6 @@ internal PropertyDescriptorCollection GetPropertiesInternal() /// descriptor implementation should return the default object, that is the main /// object that exposes the properties and attributes, /// - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs b/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs index 63dbd993fea70..4b13c79f5eba7 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs @@ -121,7 +121,7 @@ internal void LoadTypeValues(XmlSchemaSimpleType node) } } - string tempStr = XSDSchema.GetMsdataAttribute(node, Keywords.TARGETNAMESPACE); + string? tempStr = XSDSchema.GetMsdataAttribute(node, Keywords.TARGETNAMESPACE); if (tempStr != null) _ns = tempStr; } @@ -129,7 +129,7 @@ internal void LoadTypeValues(XmlSchemaSimpleType node) internal bool IsPlainString() { return ( - XSDSchema.QualifiedName(_baseType) == XSDSchema.QualifiedName("string") && + XSDSchema.QualifiedName(_baseType!) == XSDSchema.QualifiedName("string") && string.IsNullOrEmpty(_name) && _length == -1 && _minLength == -1 && diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs b/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs index 12502ad9da104..df87d51e94541 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Diagnostics; using System.Collections; using System.Xml; @@ -14,9 +11,9 @@ namespace System.Data { internal sealed class XMLDiffLoader { - private ArrayList _tables; - private DataSet _dataSet; - private DataTable _dataTable; + private ArrayList? _tables; + private DataSet? _dataSet; + private DataTable? _dataTable; [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void LoadDiffGram(DataSet ds, XmlReader dataTextReader) @@ -42,7 +39,7 @@ private void CreateTablesHierarchy(DataTable dt) { foreach (DataRelation r in dt.ChildRelations) { - if (!_tables.Contains(r.ChildTable)) + if (!_tables!.Contains(r.ChildTable)) { _tables.Add(r.ChildTable); CreateTablesHierarchy(r.ChildTable); @@ -76,8 +73,8 @@ internal void LoadDiffGram(DataTable dt, XmlReader dataTextReader) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessDiffs(DataSet ds, XmlReader ssync) { - DataTable tableBefore; - DataRow row; + DataTable? tableBefore; + DataRow? row; int oldRowRecord; int pos = -1; @@ -89,13 +86,13 @@ internal void ProcessDiffs(DataSet ds, XmlReader ssync) while (iSsyncDepth < ssync.Depth) { tableBefore = null; - string diffId = null; + string? diffId = null; oldRowRecord = -1; // the diffgramm always contains sql:before and sql:after pairs - diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; bool hasErrors = ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE; oldRowRecord = ReadOldRowData(ds, ref tableBefore, ref pos, ssync); if (oldRowRecord == -1) @@ -104,7 +101,7 @@ internal void ProcessDiffs(DataSet ds, XmlReader ssync) if (tableBefore == null) throw ExceptionBuilder.DiffgramMissingSQL(); - row = (DataRow)tableBefore.RowDiffId[diffId]; + row = (DataRow?)tableBefore.RowDiffId[diffId]; if (row != null) { row._oldRecord = oldRowRecord; @@ -129,8 +126,8 @@ internal void ProcessDiffs(DataSet ds, XmlReader ssync) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) { - DataTable tableBefore; - DataRow row; + DataTable? tableBefore; + DataRow? row; int oldRowRecord; int pos = -1; @@ -142,13 +139,13 @@ internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) while (iSsyncDepth < ssync.Depth) { tableBefore = null; - string diffId = null; + string diffId; oldRowRecord = -1; // the diffgramm always contains sql:before and sql:after pairs - diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; bool hasErrors = ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE; oldRowRecord = ReadOldRowData(_dataSet, ref tableBefore, ref pos, ssync); if (oldRowRecord == -1) @@ -157,7 +154,7 @@ internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) if (tableBefore == null) throw ExceptionBuilder.DiffgramMissingSQL(); - row = (DataRow)tableBefore.RowDiffId[diffId]; + row = (DataRow?)tableBefore.RowDiffId[diffId]; if (row != null) { @@ -183,7 +180,7 @@ internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessErrors(DataSet ds, XmlReader ssync) { - DataTable table; + DataTable? table; int iSsyncDepth = ssync.Depth; ssync.Read(); // pass the before node. @@ -193,9 +190,9 @@ internal void ProcessErrors(DataSet ds, XmlReader ssync) table = ds.Tables.GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI); if (table == null) throw ExceptionBuilder.DiffgramMissingSQL(); - string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); - DataRow row = (DataRow)table.RowDiffId[diffId]; - string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; + DataRow row = (DataRow)table.RowDiffId[diffId]!; + string? rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); if (rowError != null) row.RowError = rowError; int iRowDepth = ssync.Depth; @@ -204,10 +201,10 @@ internal void ProcessErrors(DataSet ds, XmlReader ssync) { if (XmlNodeType.Element == ssync.NodeType) { - DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]; + DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]!; //if (col == null) // throw exception here - string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS)!; row.SetColumnError(col, colError); } @@ -223,7 +220,7 @@ internal void ProcessErrors(DataSet ds, XmlReader ssync) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessErrors(ArrayList dt, XmlReader ssync) { - DataTable table; + DataTable? table; int iSsyncDepth = ssync.Depth; ssync.Read(); // pass the before node. @@ -234,14 +231,14 @@ internal void ProcessErrors(ArrayList dt, XmlReader ssync) if (table == null) throw ExceptionBuilder.DiffgramMissingSQL(); - string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; - DataRow row = (DataRow)table.RowDiffId[diffId]; + DataRow? row = (DataRow?)table.RowDiffId[diffId]; if (row == null) { for (int i = 0; i < dt.Count; i++) { - row = (DataRow)((DataTable)dt[i]).RowDiffId[diffId]; + row = (DataRow?)((DataTable)dt[i]!).RowDiffId[diffId]; if (row != null) { table = row.Table; @@ -249,9 +246,9 @@ internal void ProcessErrors(ArrayList dt, XmlReader ssync) } } } - string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string? rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); if (rowError != null) - row.RowError = rowError; + row!.RowError = rowError; int iRowDepth = ssync.Depth; ssync.Read(); // we may be inside a column @@ -259,11 +256,11 @@ internal void ProcessErrors(ArrayList dt, XmlReader ssync) { if (XmlNodeType.Element == ssync.NodeType) { - DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]; + DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]!; //if (col == null) // throw exception here - string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); - row.SetColumnError(col, colError); + string? colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + row!.SetColumnError(col, colError); } ssync.Read(); } @@ -273,17 +270,17 @@ internal void ProcessErrors(ArrayList dt, XmlReader ssync) return; } - private DataTable GetTable(string tableName, string ns) + private DataTable? GetTable(string tableName, string ns) { if (_tables == null) - return _dataSet.Tables.GetTable(tableName, ns); + return _dataSet!.Tables.GetTable(tableName, ns); if (_tables.Count == 0) - return (DataTable)_tables[0]; + return (DataTable?)_tables[0]; for (int i = 0; i < _tables.Count; i++) { - DataTable dt = (DataTable)_tables[i]; + DataTable dt = (DataTable)_tables[i]!; if (string.Equals(dt.TableName, tableName, StringComparison.Ordinal) && string.Equals(dt.Namespace, ns, StringComparison.Ordinal)) return dt; @@ -292,7 +289,7 @@ private DataTable GetTable(string tableName, string ns) } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlReader row) + private int ReadOldRowData(DataSet? ds, ref DataTable? table, ref int pos, XmlReader row) { // read table information if (ds != null) @@ -311,7 +308,7 @@ private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlRead } int iRowDepth = row.Depth; - string value = null; + string? value = null; value = row.GetAttribute(Keywords.ROWORDER, Keywords.MSDNS); if (!string.IsNullOrEmpty(value)) @@ -376,7 +373,7 @@ private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlRead { string ln = XmlConvert.DecodeName(row.LocalName); string ns = row.NamespaceURI; - DataColumn column = table.Columns[ln, ns]; + DataColumn? column = table.Columns[ln, ns]; if (column == null) { @@ -394,13 +391,13 @@ private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlRead (row.GetAttribute(Keywords.TYPE, Keywords.XSINS) != null)); bool skipped = false; - if (column.Table.DataSet != null && column.Table.DataSet._udtIsWrapped) + if (column.Table!.DataSet != null && column.Table.DataSet._udtIsWrapped) { row.Read(); // if UDT is wrapped, skip the wrapper skipped = true; } - XmlRootAttribute xmlAttrib = null; + XmlRootAttribute? xmlAttrib = null; if (!isPolymorphism && !column.ImplementsIXMLSerializable) { // THIS CHECK MAY BE IS WRONG think more diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs index f7cfd54efe8c7..1094e6ed536e9 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Data.Common; using System.Xml; using System.Xml.Schema; @@ -44,14 +41,14 @@ internal static void SetProperties(object instance, XmlAttributeCollection attrs if (name == "Expression" && instance is DataColumn) continue; - PropertyDescriptor pd = TypeDescriptor.GetProperties(instance)[name]; + PropertyDescriptor? pd = TypeDescriptor.GetProperties(instance)[name]; if (pd != null) { // Standard property Type type = pd.PropertyType; TypeConverter converter = XMLSchema.GetConverter(type); - object propValue; + object? propValue; if (converter.CanConvertFrom(typeof(string))) { propValue = converter.ConvertFromInvariantString(value); @@ -74,7 +71,7 @@ internal static void SetProperties(object instance, XmlAttributeCollection attrs } }// SetProperties - internal static bool FEqualIdentity(XmlNode node, string name, string ns) + internal static bool FEqualIdentity(XmlNode? node, string name, string ns) { if (node != null && node.LocalName == name && node.NamespaceURI == ns) return true; @@ -135,26 +132,26 @@ public ConstraintTable(DataTable t, XmlSchemaIdentityConstraint c) internal sealed class XSDSchema : XMLSchema { - private XmlSchemaSet _schemaSet; - private XmlSchemaElement _dsElement; - private DataSet _ds; - private string _schemaName; - private ArrayList _columnExpressions; - private Hashtable _constraintNodes; - private ArrayList _refTables; - private ArrayList _complexTypes; - private XmlSchemaObjectCollection _annotations; - private XmlSchemaObjectCollection _elements; - private Hashtable _attributes; - private Hashtable _elementsTable; - private Hashtable _attributeGroups; - private Hashtable _schemaTypes; - private Hashtable _expressions; - private Dictionary> _tableDictionary; - - private Hashtable _udSimpleTypes; - - private Hashtable _existingSimpleTypeMap; + private XmlSchemaSet? _schemaSet; + private XmlSchemaElement? _dsElement; + private DataSet? _ds; + private string? _schemaName; + private ArrayList? _columnExpressions; + private Hashtable? _constraintNodes; + private ArrayList? _refTables; + private ArrayList? _complexTypes; + private XmlSchemaObjectCollection? _annotations; + private XmlSchemaObjectCollection? _elements; + private Hashtable? _attributes; + private Hashtable? _elementsTable; + private Hashtable? _attributeGroups; + private Hashtable? _schemaTypes; + private Hashtable? _expressions; + private Dictionary>? _tableDictionary; + + private Hashtable? _udSimpleTypes; + + private Hashtable? _existingSimpleTypeMap; private bool _fromInference; @@ -189,38 +186,38 @@ private void CollectElementsAnnotations(XmlSchema schema, ArrayList schemaList) { if (item is XmlSchemaAnnotation) { - _annotations.Add((XmlSchemaAnnotation)item); + _annotations!.Add((XmlSchemaAnnotation)item); } if (item is XmlSchemaElement) { XmlSchemaElement elem = (XmlSchemaElement)item; - _elements.Add(elem); - _elementsTable[elem.QualifiedName] = elem; + _elements!.Add(elem); + _elementsTable![elem.QualifiedName] = elem; } if (item is XmlSchemaAttribute) { XmlSchemaAttribute attr = (XmlSchemaAttribute)item; - _attributes[attr.QualifiedName] = attr; + _attributes![attr.QualifiedName] = attr; } if (item is XmlSchemaAttributeGroup) { XmlSchemaAttributeGroup attr = (XmlSchemaAttributeGroup)item; - _attributeGroups[attr.QualifiedName] = attr; + _attributeGroups![attr.QualifiedName] = attr; } if (item is XmlSchemaType) { - string MSDATATargetNamespace = null; + string? MSDATATargetNamespace = null; if (item is XmlSchemaSimpleType) { MSDATATargetNamespace = XSDSchema.GetMsdataAttribute((XmlSchemaType)item, Keywords.TARGETNAMESPACE); } XmlSchemaType type = (XmlSchemaType)item; - _schemaTypes[type.QualifiedName] = type; + _schemaTypes![type.QualifiedName] = type; // if we have a User Defined simple type, cache it so later we may need for mapping // meanwhile more convinient solution would be to directly use schemaTypes, but it would be more complex to handle - XmlSchemaSimpleType xmlSimpleType = (item as XmlSchemaSimpleType); + XmlSchemaSimpleType? xmlSimpleType = (item as XmlSchemaSimpleType); if (xmlSimpleType != null) { if (_udSimpleTypes == null) @@ -229,9 +226,9 @@ private void CollectElementsAnnotations(XmlSchema schema, ArrayList schemaList) } _udSimpleTypes[type.QualifiedName.ToString()] = xmlSimpleType; - DataColumn dc = (DataColumn)_existingSimpleTypeMap[type.QualifiedName.ToString()]; + DataColumn? dc = (DataColumn?)_existingSimpleTypeMap![type.QualifiedName.ToString()]; // Assumption is that our simple type qualified name ihas the same output as XmlSchemaSimpleType type.QualifiedName.ToString() - SimpleType tmpSimpleType = (dc != null) ? dc.SimpleType : null; + SimpleType? tmpSimpleType = (dc != null) ? dc.SimpleType : null; if (tmpSimpleType != null) { @@ -239,7 +236,7 @@ private void CollectElementsAnnotations(XmlSchema schema, ArrayList schemaList) string errorStr = tmpSimpleType.HasConflictingDefinition(tmpDataSimpleType); if (errorStr.Length != 0) { - throw ExceptionBuilder.InvalidDuplicateNamedSimpleTypeDelaration(tmpDataSimpleType.SimpleTypeQualifiedName, errorStr); + throw ExceptionBuilder.InvalidDuplicateNamedSimpleTypeDelaration(tmpDataSimpleType.SimpleTypeQualifiedName!, errorStr); } } } @@ -265,7 +262,7 @@ internal static string QualifiedName(string name) } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal static void SetProperties(object instance, XmlAttribute[] attrs) + internal static void SetProperties(object instance, XmlAttribute[]? attrs) { // This is called from both XSD and XDR schemas. // Do we realy need it in XSD ??? @@ -286,7 +283,7 @@ internal static void SetProperties(object instance, XmlAttribute[] attrs) if (name == "DataType") { - DataColumn col = instance as DataColumn; + DataColumn? col = instance as DataColumn; if (col != null) { col.DataType = DataStorage.GetType(value); @@ -295,14 +292,14 @@ internal static void SetProperties(object instance, XmlAttribute[] attrs) continue; } - PropertyDescriptor pd = TypeDescriptor.GetProperties(instance)[name]; + PropertyDescriptor? pd = TypeDescriptor.GetProperties(instance)[name]; if (pd != null) { // Standard property Type type = pd.PropertyType; TypeConverter converter = XMLSchema.GetConverter(type); - object propValue; + object? propValue; if (converter.CanConvertFrom(typeof(string))) { propValue = converter.ConvertFromInvariantString(value); @@ -326,9 +323,9 @@ internal static void SetProperties(object instance, XmlAttribute[] attrs) }// SetProperties [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private static void SetExtProperties(object instance, XmlAttribute[] attrs) + private static void SetExtProperties(object instance, XmlAttribute[]? attrs) { - PropertyCollection props = null; + PropertyCollection? props = null; if (attrs == null) return; for (int i = 0; i < attrs.Length; i++) @@ -337,7 +334,7 @@ private static void SetExtProperties(object instance, XmlAttribute[] attrs) { if (props == null) { - object val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"].GetValue(instance); + object? val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"]!.GetValue(instance); Debug.Assert(val is PropertyCollection, "We can set values only for classes that have ExtendedProperties"); props = (PropertyCollection)val; } @@ -364,11 +361,11 @@ private static void SetExtProperties(object instance, XmlAttribute[] attrs) } }// SetExtProperties - private void HandleColumnExpression(object instance, XmlAttribute[] attrs) + private void HandleColumnExpression(object instance, XmlAttribute[]? attrs) { if (attrs == null) return; - DataColumn dc = instance as DataColumn; + DataColumn? dc = instance as DataColumn; Debug.Assert(dc != null, "HandleColumnExpression is supposed to be called for DataColumn"); if (dc != null) { @@ -381,7 +378,7 @@ private void HandleColumnExpression(object instance, XmlAttribute[] attrs) if (_expressions == null) _expressions = new Hashtable(); _expressions[dc] = attrs[i].Value; - _columnExpressions.Add(dc); + _columnExpressions!.Add(dc); break; } } @@ -389,9 +386,9 @@ private void HandleColumnExpression(object instance, XmlAttribute[] attrs) } } - internal static string GetMsdataAttribute(XmlSchemaAnnotated node, string ln) + internal static string? GetMsdataAttribute(XmlSchemaAnnotated node, string ln) { - XmlAttribute[] nodeAttributes = node.UnhandledAttributes; + XmlAttribute[]? nodeAttributes = node.UnhandledAttributes; if (nodeAttributes != null) for (int i = 0; i < nodeAttributes.Length; i++) if (nodeAttributes[i].LocalName == ln && nodeAttributes[i].NamespaceURI == Keywords.MSDNS) @@ -402,14 +399,14 @@ internal static string GetMsdataAttribute(XmlSchemaAnnotated node, string ln) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private static void SetExtProperties(object instance, XmlAttributeCollection attrs) { - PropertyCollection props = null; + PropertyCollection? props = null; for (int i = 0; i < attrs.Count; i++) { if (attrs[i].NamespaceURI == Keywords.MSPROPNS) { if (props == null) { - object val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"].GetValue(instance); + object? val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"]!.GetValue(instance); Debug.Assert(val is PropertyCollection, "We can set values only for classes that have ExtendedProperties"); props = (PropertyCollection)val; } @@ -423,7 +420,7 @@ private static void SetExtProperties(object instance, XmlAttributeCollection att internal void HandleRefTableProperties(ArrayList RefTables, XmlSchemaElement element) { string typeName = GetInstanceName(element); - DataTable table = _ds.Tables.GetTable(XmlConvert.DecodeName(typeName), element.QualifiedName.Namespace); + DataTable? table = _ds!.Tables.GetTable(XmlConvert.DecodeName(typeName), element.QualifiedName.Namespace); Debug.Assert(table != null, "ref table should have been already created"); SetProperties(table, element.UnhandledAttributes); @@ -441,12 +438,12 @@ internal void HandleRelation(XmlElement node, bool fNested) string value; bool fCreateConstraints = false; //if we have a relation, //we do not have constraints - DataRelationCollection rels = _ds.Relations; + DataRelationCollection rels = _ds!.Relations; DataRelation relation; DataColumn[] parentKey; DataColumn[] childKey; - DataTable parent; - DataTable child; + DataTable? parent; + DataTable? child; int keyLength; strName = XmlConvert.DecodeName(node.GetAttribute(Keywords.NAME)); @@ -499,10 +496,10 @@ internal void HandleRelation(XmlElement node, bool fNested) for (int i = 0; i < keyLength; i++) { - parentKey[i] = parent.Columns[XmlConvert.DecodeName(parentNames[i])]; + parentKey[i] = parent.Columns[XmlConvert.DecodeName(parentNames[i])]!; if (parentKey[i] == null) throw ExceptionBuilder.ElementTypeNotFound(parentNames[i]); - childKey[i] = child.Columns[XmlConvert.DecodeName(childNames[i])]; + childKey[i] = child.Columns[XmlConvert.DecodeName(childNames[i])]!; if (childKey[i] == null) throw ExceptionBuilder.ElementTypeNotFound(childNames[i]); } @@ -512,7 +509,7 @@ internal void HandleRelation(XmlElement node, bool fNested) _ds.Relations.Add(relation); if (FromInference && relation.Nested) { - _tableDictionary[relation.ParentTable].Add(relation.ChildTable); + _tableDictionary![relation.ParentTable].Add(relation.ChildTable); } } @@ -538,7 +535,7 @@ private bool HasAttributes(XmlSchemaObjectCollection attributes) private bool IsDatasetParticle(XmlSchemaParticle pt) { - XmlSchemaObjectCollection items = GetParticleItems(pt); + XmlSchemaObjectCollection? items = GetParticleItems(pt); if (items == null) return false; // empty element, threat it as table @@ -590,7 +587,7 @@ private int DatasetElementCount(XmlSchemaObjectCollection elements) return nCount; } - private XmlSchemaElement FindDatasetElement(XmlSchemaObjectCollection elements) + private XmlSchemaElement? FindDatasetElement(XmlSchemaObjectCollection elements) { foreach (XmlSchemaElement XmlElement in elements) { @@ -603,7 +600,7 @@ private XmlSchemaElement FindDatasetElement(XmlSchemaObjectCollection elements) if (!GetBooleanAttribute(node, Keywords.MSD_ISDATASET, /*default:*/ true)) return null; - XmlSchemaComplexType ct = node.SchemaType as XmlSchemaComplexType; + XmlSchemaComplexType? ct = node.SchemaType as XmlSchemaComplexType; if (ct == null) return null; @@ -614,7 +611,7 @@ private XmlSchemaElement FindDatasetElement(XmlSchemaObjectCollection elements) if (ct.ContentModel is XmlSchemaSimpleContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated? cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; if (cContent is XmlSchemaSimpleContentExtension) { XmlSchemaSimpleContentExtension ccExtension = ((XmlSchemaSimpleContentExtension)cContent); @@ -623,14 +620,14 @@ private XmlSchemaElement FindDatasetElement(XmlSchemaObjectCollection elements) } else { - XmlSchemaSimpleContentRestriction ccRestriction = ((XmlSchemaSimpleContentRestriction)cContent); + XmlSchemaSimpleContentRestriction ccRestriction = ((XmlSchemaSimpleContentRestriction)cContent!); if (HasAttributes(ccRestriction.Attributes)) return null; } } - XmlSchemaParticle particle = GetParticle(ct); + XmlSchemaParticle? particle = GetParticle(ct); if (particle != null) { if (!IsDatasetParticle(particle)) @@ -682,7 +679,7 @@ public void LoadSchema(XmlSchemaSet schemaSet, DataSet ds) _schemaName = "NewDataSet"; } ds.DataSetName = XmlConvert.DecodeName(_schemaName); - string ns = schemaRoot.TargetNamespace; + string? ns = schemaRoot.TargetNamespace; if (ds._namespaceURI == null || ds._namespaceURI.Length == 0) {// set just one time, for backward compatibility ds._namespaceURI = (ns == null) ? string.Empty : ns; // see fx\Data\XDO\ReadXml\SchemaM2.xml for more info @@ -753,15 +750,15 @@ public void LoadSchema(XmlSchemaSet schemaSet, DataSet ds) throw ExceptionBuilder.TooManyIsDataSetAttributesInSchema(); } - XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(_dsElement); + XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(_dsElement!)!; if (ct.Particle != null) { - XmlSchemaObjectCollection items = GetParticleItems(ct.Particle); + XmlSchemaObjectCollection? items = GetParticleItems(ct.Particle); if (items != null) { foreach (XmlSchemaAnnotated el in items) { - XmlSchemaElement sel = el as XmlSchemaElement; + XmlSchemaElement? sel = el as XmlSchemaElement; if (null != sel) { if (sel.RefName.Name.Length != 0) @@ -812,11 +809,11 @@ public void LoadSchema(XmlSchemaSet schemaSet, DataSet ds) //just add Expressions, at this point and if ColumnExpressions.Count > 0, this.expressions should not be null for (int i = 0; i < _columnExpressions.Count; i++) { - DataColumn dc = ((DataColumn)(_columnExpressions[i])); - dc.Expression = (string)_expressions[dc]; + DataColumn dc = ((DataColumn)(_columnExpressions[i])!); + dc.Expression = (string)_expressions![dc]!; } - foreach (DataTable dt in ds.Tables) + foreach (DataTable dt in ds!.Tables) { if (dt.NestedParentRelations.Length == 0 && dt.Namespace == ds.Namespace) { @@ -834,14 +831,14 @@ public void LoadSchema(XmlSchemaSet schemaSet, DataSet ds) } } - DataTable tmpTable = ds.Tables[ds.DataSetName, ds.Namespace]; + DataTable? tmpTable = ds.Tables[ds.DataSetName, ds.Namespace]; if (tmpTable != null) // this fix is done to support round-trip problem in case if there is one table with same name and NS tmpTable._fNestedInDataset = true; // this fix is for backward compatability with old inference engine if (FromInference && ds.Tables.Count == 0 && string.Equals(ds.DataSetName, "NewDataSet", StringComparison.Ordinal)) - ds.DataSetName = XmlConvert.DecodeName(((XmlSchemaElement)_elements[0]).Name); + ds.DataSetName = XmlConvert.DecodeName(((XmlSchemaElement)_elements[0]).Name)!; ds._fIsSchemaLoading = false; //reactivate column computations @@ -872,14 +869,14 @@ private void HandleRelations(XmlSchemaAnnotation ann, bool fNested) foreach (object __items in ann.Items) if (__items is XmlSchemaAppInfo) { - XmlNode[] relations = ((XmlSchemaAppInfo)__items).Markup; + XmlNode[] relations = ((XmlSchemaAppInfo)__items).Markup!; for (int i = 0; i < relations.Length; i++) if (FEqualIdentity(relations[i], Keywords.MSD_RELATION, Keywords.MSDNS)) HandleRelation((XmlElement)relations[i], fNested); } } - internal XmlSchemaObjectCollection GetParticleItems(XmlSchemaParticle pt) + internal XmlSchemaObjectCollection? GetParticleItems(XmlSchemaParticle? pt) { if (pt is XmlSchemaSequence) return ((XmlSchemaSequence)pt).Items; @@ -905,21 +902,21 @@ internal XmlSchemaObjectCollection GetParticleItems(XmlSchemaParticle pt) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleParticle(XmlSchemaParticle pt, DataTable table, ArrayList tableChildren, bool isBase) { - XmlSchemaObjectCollection items = GetParticleItems(pt); + XmlSchemaObjectCollection? items = GetParticleItems(pt); if (items == null) return; foreach (XmlSchemaAnnotated item in items) { - XmlSchemaElement el = item as XmlSchemaElement; + XmlSchemaElement? el = item as XmlSchemaElement; if (el != null) { if (FromInference && pt is XmlSchemaChoice && pt.MaxOccurs > decimal.One && (el.SchemaType is XmlSchemaComplexType)) el.MaxOccurs = pt.MaxOccurs; - DataTable child = null; + DataTable? child = null; // to decide if element is our table, we need to match both name and ns // 286043 - SQL BU Defect Tracking if (((el.Name == null) && (el.RefName.Name == table.EncodedTableName && el.RefName.Namespace == table.Namespace)) || @@ -953,7 +950,7 @@ internal void HandleParticle(XmlSchemaParticle pt, DataTable table, ArrayList ta } else { - DataRelation relation = null; + DataRelation? relation = null; if (el.Annotation != null) HandleRelations(el.Annotation, true); @@ -1002,8 +999,8 @@ internal void HandleAttributes(XmlSchemaObjectCollection attributes, DataTable t } else { // XmlSchemaAttributeGroupRef - XmlSchemaAttributeGroupRef groupRef = so as XmlSchemaAttributeGroupRef; - XmlSchemaAttributeGroup schemaGroup = _attributeGroups[groupRef.RefName] as XmlSchemaAttributeGroup; + XmlSchemaAttributeGroupRef? groupRef = so as XmlSchemaAttributeGroupRef; + XmlSchemaAttributeGroup? schemaGroup = _attributeGroups![groupRef!.RefName] as XmlSchemaAttributeGroup; if (schemaGroup != null) { HandleAttributeGroup(schemaGroup, table, isBase); @@ -1024,14 +1021,14 @@ private void HandleAttributeGroup(XmlSchemaAttributeGroup attributeGroup, DataTa else { // XmlSchemaAttributeGroupRef XmlSchemaAttributeGroupRef attributeGroupRef = (XmlSchemaAttributeGroupRef)obj; - XmlSchemaAttributeGroup attributeGroupResolved; + XmlSchemaAttributeGroup? attributeGroupResolved; if (attributeGroup.RedefinedAttributeGroup != null && attributeGroupRef.RefName == new XmlQualifiedName(attributeGroup.Name, attributeGroupRef.RefName.Namespace)) { attributeGroupResolved = attributeGroup.RedefinedAttributeGroup; } else { - attributeGroupResolved = (XmlSchemaAttributeGroup)_attributeGroups[attributeGroupRef.RefName]; + attributeGroupResolved = (XmlSchemaAttributeGroup?)_attributeGroups![attributeGroupRef.RefName]; } if (attributeGroupResolved != null) { @@ -1044,8 +1041,8 @@ private void HandleAttributeGroup(XmlSchemaAttributeGroup attributeGroup, DataTa [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayList tableChildren, bool isNillable) { - if (_complexTypes.Contains(ct)) - throw ExceptionBuilder.CircularComplexType(ct.Name); + if (_complexTypes!.Contains(ct)) + throw ExceptionBuilder.CircularComplexType(ct.Name!); bool isBase = false; _complexTypes.Add(ct); @@ -1062,7 +1059,7 @@ internal void HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayL if (ct.ContentModel is XmlSchemaComplexContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated? cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; if (cContent is XmlSchemaComplexContentExtension) { XmlSchemaComplexContentExtension ccExtension = ((XmlSchemaComplexContentExtension)cContent); @@ -1106,7 +1103,7 @@ internal void HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayL else { Debug.Assert(ct.ContentModel is XmlSchemaSimpleContent, "expected simpleContent or complexContent"); - XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content!; if (cContent is XmlSchemaSimpleContentExtension) { XmlSchemaSimpleContentExtension ccExtension = ((XmlSchemaSimpleContentExtension)cContent); @@ -1147,13 +1144,13 @@ internal void HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayL _complexTypes.Remove(ct); } - internal XmlSchemaParticle GetParticle(XmlSchemaComplexType ct) + internal XmlSchemaParticle? GetParticle(XmlSchemaComplexType ct) { if (ct.ContentModel != null) { if (ct.ContentModel is XmlSchemaComplexContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content!; if (cContent is XmlSchemaComplexContentExtension) { return ((XmlSchemaComplexContentExtension)cContent).Particle; @@ -1191,7 +1188,7 @@ internal DataColumn FindField(DataTable table, string field) colName = split[split.Length - 1]; colName = XmlConvert.DecodeName(colName); - DataColumn col = table.Columns[colName]; + DataColumn? col = table.Columns[colName]; if (col == null) throw ExceptionBuilder.InvalidField(field); @@ -1209,7 +1206,7 @@ internal DataColumn[] BuildKey(XmlSchemaIdentityConstraint keyNode, DataTable ta foreach (XmlSchemaXPath node in keyNode.Fields) { - keyColumns.Add(FindField(table, node.XPath)); + keyColumns.Add(FindField(table, node.XPath!)); } DataColumn[] key = new DataColumn[keyColumns.Count]; @@ -1220,7 +1217,7 @@ internal DataColumn[] BuildKey(XmlSchemaIdentityConstraint keyNode, DataTable ta internal bool GetBooleanAttribute(XmlSchemaAnnotated element, string attrName, bool defVal) { - string value = GetMsdataAttribute(element, attrName); + string? value = GetMsdataAttribute(element, attrName); if (value == null || value.Length == 0) { return defVal; @@ -1239,7 +1236,7 @@ internal bool GetBooleanAttribute(XmlSchemaAnnotated element, string attrName, b internal string GetStringAttribute(XmlSchemaAnnotated element, string attrName, string defVal) { - string value = GetMsdataAttribute(element, attrName); + string? value = GetMsdataAttribute(element, attrName); if (value == null || value.Length == 0) { return defVal; @@ -1258,7 +1255,7 @@ internal string GetStringAttribute(XmlSchemaAnnotated element, string attrName, */ - internal static AcceptRejectRule TranslateAcceptRejectRule(string strRule) + internal static AcceptRejectRule TranslateAcceptRejectRule(string? strRule) { if (strRule == "Cascade") return AcceptRejectRule.Cascade; @@ -1286,16 +1283,16 @@ internal static Rule TranslateRule(string strRule) internal void HandleKeyref(XmlSchemaKeyref keyref) { string refer = XmlConvert.DecodeName(keyref.Refer.Name); // check here!!! - string name = XmlConvert.DecodeName(keyref.Name); + string name = XmlConvert.DecodeName(keyref.Name)!; name = GetStringAttribute(keyref, "ConstraintName", /*default:*/ name); // we do not process key defined outside the current node string tableName = GetTableName(keyref); - string tableNs = GetMsdataAttribute(keyref, Keywords.MSD_TABLENS); + string? tableNs = GetMsdataAttribute(keyref, Keywords.MSD_TABLENS); - DataTable table = _ds.Tables.GetTableSmart(tableName, tableNs); + DataTable? table = _ds!.Tables.GetTableSmart(tableName, tableNs); if (table == null) return; @@ -1303,7 +1300,7 @@ internal void HandleKeyref(XmlSchemaKeyref keyref) if (refer == null || refer.Length == 0) throw ExceptionBuilder.MissingRefer(name); - ConstraintTable key = (ConstraintTable)_constraintNodes[refer]; + ConstraintTable? key = (ConstraintTable?)_constraintNodes![refer]; if (key == null) { @@ -1313,57 +1310,57 @@ internal void HandleKeyref(XmlSchemaKeyref keyref) DataColumn[] pKey = BuildKey(key.constraint, key.table); DataColumn[] fKey = BuildKey(keyref, table); - ForeignKeyConstraint fkc = null; + ForeignKeyConstraint? fkc = null; if (GetBooleanAttribute(keyref, Keywords.MSD_CONSTRAINTONLY, /*default:*/ false)) { - int iExisting = fKey[0].Table.Constraints.InternalIndexOf(name); + int iExisting = fKey[0].Table!.Constraints.InternalIndexOf(name); if (iExisting > -1) { - if (fKey[0].Table.Constraints[iExisting].ConstraintName != name) + if (fKey[0].Table!.Constraints[iExisting].ConstraintName != name) iExisting = -1; } if (iExisting < 0) { fkc = new ForeignKeyConstraint(name, pKey, fKey); - fKey[0].Table.Constraints.Add(fkc); + fKey[0].Table!.Constraints.Add(fkc); } } else { - string relName = XmlConvert.DecodeName(GetStringAttribute(keyref, Keywords.MSD_RELATIONNAME, keyref.Name)); + string relName = XmlConvert.DecodeName(GetStringAttribute(keyref, Keywords.MSD_RELATIONNAME, keyref.Name!)); if (relName == null || relName.Length == 0) relName = name; - int iExisting = fKey[0].Table.DataSet.Relations.InternalIndexOf(relName); + int iExisting = fKey[0].Table!.DataSet!.Relations.InternalIndexOf(relName); if (iExisting > -1) { - if (fKey[0].Table.DataSet.Relations[iExisting].RelationName != relName) + if (fKey[0].Table!.DataSet!.Relations[iExisting].RelationName != relName) iExisting = -1; } - DataRelation relation = null; + DataRelation? relation = null; if (iExisting < 0) { relation = new DataRelation(relName, pKey, fKey); SetExtProperties(relation, keyref.UnhandledAttributes); - pKey[0].Table.DataSet.Relations.Add(relation); + pKey[0].Table!.DataSet!.Relations.Add(relation); if (FromInference && relation.Nested) { - if (_tableDictionary.ContainsKey(relation.ParentTable)) + if (_tableDictionary!.ContainsKey(relation.ParentTable)) { _tableDictionary[relation.ParentTable].Add(relation.ChildTable); } } - fkc = relation.ChildKeyConstraint; + fkc = relation.ChildKeyConstraint!; fkc.ConstraintName = name; } else { - relation = fKey[0].Table.DataSet.Relations[iExisting]; + relation = fKey[0].Table!.DataSet!.Relations[iExisting]; } if (GetBooleanAttribute(keyref, Keywords.MSD_ISNESTED, /*default:*/ false)) { @@ -1371,9 +1368,9 @@ internal void HandleKeyref(XmlSchemaKeyref keyref) } } - string acceptRejectRule = GetMsdataAttribute(keyref, Keywords.MSD_ACCEPTREJECTRULE); - string updateRule = GetMsdataAttribute(keyref, Keywords.MSD_UPDATERULE); - string deleteRule = GetMsdataAttribute(keyref, Keywords.MSD_DELETERULE); + string? acceptRejectRule = GetMsdataAttribute(keyref, Keywords.MSD_ACCEPTREJECTRULE); + string? updateRule = GetMsdataAttribute(keyref, Keywords.MSD_UPDATERULE); + string? deleteRule = GetMsdataAttribute(keyref, Keywords.MSD_DELETERULE); if (fkc != null) { @@ -1393,20 +1390,20 @@ internal void HandleKeyref(XmlSchemaKeyref keyref) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleConstraint(XmlSchemaIdentityConstraint keyNode) { - string name = null; + string? name = null; name = XmlConvert.DecodeName(keyNode.Name); if (name == null || name.Length == 0) throw ExceptionBuilder.MissingAttribute(Keywords.NAME); - if (_constraintNodes.ContainsKey(name)) + if (_constraintNodes!.ContainsKey(name)) throw ExceptionBuilder.DuplicateConstraintRead(name); // we do not process key defined outside the current node string tableName = GetTableName(keyNode); - string tableNs = GetMsdataAttribute(keyNode, Keywords.MSD_TABLENS); + string? tableNs = GetMsdataAttribute(keyNode, Keywords.MSD_TABLENS); - DataTable table = _ds.Tables.GetTableSmart(tableName, tableNs); + DataTable? table = _ds!.Tables.GetTableSmart(tableName, tableNs); if (table == null) return; @@ -1422,19 +1419,19 @@ internal void HandleConstraint(XmlSchemaIdentityConstraint keyNode) if (0 < key.Length) { - UniqueConstraint found = (UniqueConstraint)key[0].Table.Constraints.FindConstraint(new UniqueConstraint(name, key)); + UniqueConstraint? found = (UniqueConstraint?)key[0].Table!.Constraints.FindConstraint(new UniqueConstraint(name, key)); if (found == null) { - key[0].Table.Constraints.Add(name, key, fPrimaryKey); - SetExtProperties(key[0].Table.Constraints[name], keyNode.UnhandledAttributes); + key[0].Table!.Constraints.Add(name, key, fPrimaryKey); + SetExtProperties(key[0].Table!.Constraints[name]!, keyNode.UnhandledAttributes); } else { key = found.ColumnsReference; SetExtProperties(found, keyNode.UnhandledAttributes); if (fPrimaryKey) - key[0].Table.PrimaryKey = key; + key[0].Table!.PrimaryKey = key; } if (keyNode is XmlSchemaKey) { @@ -1447,12 +1444,12 @@ internal void HandleConstraint(XmlSchemaIdentityConstraint keyNode) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal DataTable InstantiateSimpleTable(XmlSchemaElement node) { - DataTable table; + DataTable? table; string typeName = XmlConvert.DecodeName(GetInstanceName(node)); string _TableUri; _TableUri = node.QualifiedName.Namespace; - table = _ds.Tables.GetTable(typeName, _TableUri); + table = _ds!.Tables.GetTable(typeName, _TableUri); if (!FromInference && table != null) { @@ -1473,7 +1470,7 @@ internal DataTable InstantiateSimpleTable(XmlSchemaElement node) } else { - string prefix = GetPrefix(_TableUri); + string? prefix = GetPrefix(_TableUri); if (prefix != null) table.Prefix = prefix; } @@ -1483,10 +1480,10 @@ internal DataTable InstantiateSimpleTable(XmlSchemaElement node) } - XmlSchemaComplexType ct = node.SchemaType as XmlSchemaComplexType; + XmlSchemaComplexType? ct = node.SchemaType as XmlSchemaComplexType; // We assume node.ElementSchemaType.BaseSchemaType to be null for // and not null for - bool isSimpleContent = ((node.ElementSchemaType.BaseXmlSchemaType != null) || (ct != null && ct.ContentModel is XmlSchemaSimpleContent)); + bool isSimpleContent = ((node.ElementSchemaType!.BaseXmlSchemaType != null) || (ct != null && ct.ContentModel is XmlSchemaSimpleContent)); if (!FromInference || (isSimpleContent && table.Columns.Count == 0)) {// for inference backward compatability @@ -1514,7 +1511,7 @@ internal DataTable InstantiateSimpleTable(XmlSchemaElement node) _ds.Tables.Add(table); if (FromInference) { - _tableDictionary.Add(table, new List()); + _tableDictionary!.Add(table, new List()); } } @@ -1538,7 +1535,7 @@ internal DataTable InstantiateSimpleTable(XmlSchemaElement node) internal string GetInstanceName(XmlSchemaAnnotated node) { - string instanceName = null; + string? instanceName = null; Debug.Assert((node is XmlSchemaElement) || (node is XmlSchemaAttribute), "GetInstanceName should only be called on attribute or elements"); @@ -1562,7 +1559,7 @@ internal string GetInstanceName(XmlSchemaAnnotated node) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType typeNode, bool isRef) { - DataTable table; + DataTable? table; string typeName = GetInstanceName(node); ArrayList tableChildren = new ArrayList(); @@ -1570,7 +1567,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType _TableUri = node.QualifiedName.Namespace; - table = _ds.Tables.GetTable(XmlConvert.DecodeName(typeName), _TableUri); + table = _ds!.Tables.GetTable(XmlConvert.DecodeName(typeName), _TableUri); // TOD: Do not do this fix // if (table == null && node.RefName.IsEmpty && !IsTopLevelElement(node) && _TableUri != null && _TableUri.Length > 0) { // _TableUri = null; // it means form="qualified", so child element inherits namespace. amirhmy @@ -1587,7 +1584,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType } if (isRef) - _refTables.Add(_TableUri + ":" + typeName); + _refTables!.Add(_TableUri + ":" + typeName); table = new DataTable(XmlConvert.DecodeName(typeName)); table.TypeName = node.SchemaTypeName; @@ -1596,7 +1593,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType table.Namespace = GetStringAttribute(node, "targetNamespace", _TableUri); //table.Prefix = node.Prefix; - string value = GetStringAttribute(typeNode, Keywords.MSD_CASESENSITIVE, ""); + string? value = GetStringAttribute(typeNode, Keywords.MSD_CASESENSITIVE, ""); if (value.Length == 0) { value = GetStringAttribute(node, Keywords.MSD_CASESENSITIVE, ""); @@ -1632,7 +1629,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType } else { - string prefix = GetPrefix(_TableUri); + string? prefix = GetPrefix(_TableUri); if (prefix != null) table.Prefix = prefix; } @@ -1640,13 +1637,13 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType _ds.Tables.Add(table); if (FromInference) { - _tableDictionary.Add(table, new List()); + _tableDictionary!.Add(table, new List()); } } - HandleComplexType(typeNode, table, tableChildren, node.IsNillable); + HandleComplexType(typeNode, table!, tableChildren, node.IsNillable); - for (int i = 0; i < table.Columns.Count; i++) + for (int i = 0; i < table!.Columns.Count; i++) table.Columns[i].SetOrdinalInternal(i); /* @@ -1691,7 +1688,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType { foreach (XmlSchemaIdentityConstraint key in _dsElement.Constraints) { - XmlSchemaKeyref keyref = key as XmlSchemaKeyref; + XmlSchemaKeyref? keyref = key as XmlSchemaKeyref; if (keyref == null) continue; @@ -1700,7 +1697,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType continue; if (GetTableName(keyref) == _tableChild.TableName) { - if (_tableChild.DataSet.Tables.InternalIndexOf(_tableChild.TableName) < -1) + if (_tableChild.DataSet!.Tables.InternalIndexOf(_tableChild.TableName) < -1) { // if we have multiple tables with the same name if (GetTableNamespace(keyref) == _tableChild.Namespace) { @@ -1715,7 +1712,7 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType } } - DataRelation relation = null; + DataRelation? relation = null; DataRelationCollection childRelations = table.ChildRelations; for (int j = 0; j < childRelations.Count; j++) @@ -1762,17 +1759,17 @@ internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType // setup relationship between parent and this table relation = new DataRelation(table.TableName + "_" + _tableChild.TableName, parentKey, childKey, true); relation.Nested = true; - _tableChild.DataSet.Relations.Add(relation); + _tableChild.DataSet!.Relations.Add(relation); if (FromInference && relation.Nested) { - if (_tableDictionary.ContainsKey(relation.ParentTable)) + if (_tableDictionary!.ContainsKey(relation.ParentTable)) { _tableDictionary[relation.ParentTable].Add(relation.ChildTable); } } } - return (table); + return table; } private sealed class NameType : IComparable @@ -1785,7 +1782,7 @@ public NameType(string n, [DynamicallyAccessedMembers(DynamicallyAccessedMemberT name = n; type = t; } - public int CompareTo(object obj) { return string.Compare(name, (string)obj, StringComparison.Ordinal); } + public int CompareTo(object? obj) { return string.Compare(name, (string?)obj, StringComparison.Ordinal); } }; public static Type XsdtoClr(string xsdTypeName) @@ -1878,7 +1875,7 @@ private Type ParseDataType(string dt) { if (_udSimpleTypes != null) { - XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType)_udSimpleTypes[dt]; + XmlSchemaSimpleType? simpleType = (XmlSchemaSimpleType?)_udSimpleTypes[dt]; if (simpleType == null) { // it is not named simple type, it is not XSD type, it should be unsupported type like xs:token throw ExceptionBuilder.UndefinedDatatype(dt); @@ -1889,7 +1886,7 @@ private Type ParseDataType(string dt) rootType = rootType.BaseSimpleType; } - return ParseDataType(rootType.BaseType); + return ParseDataType(rootType.BaseType!); } } NameType nt = FindNameType(dt); @@ -1927,39 +1924,39 @@ internal static bool IsXsdType(string name) } - internal XmlSchemaAnnotated FindTypeNode(XmlSchemaAnnotated node) + internal XmlSchemaAnnotated? FindTypeNode(XmlSchemaAnnotated node) { // this function is returning null // if the typeNode for node is in the XSD namespace. - XmlSchemaAttribute attr = node as XmlSchemaAttribute; - XmlSchemaElement el = node as XmlSchemaElement; + XmlSchemaAttribute? attr = node as XmlSchemaAttribute; + XmlSchemaElement? el = node as XmlSchemaElement; bool isAttr = false; if (attr != null) { isAttr = true; } - string _type = isAttr ? attr.SchemaTypeName.Name : el.SchemaTypeName.Name; - string _typeNs = isAttr ? attr.SchemaTypeName.Namespace : el.SchemaTypeName.Namespace; + string _type = isAttr ? attr!.SchemaTypeName.Name : el!.SchemaTypeName.Name; + string _typeNs = isAttr ? attr!.SchemaTypeName.Namespace : el!.SchemaTypeName.Namespace; if (_typeNs == Keywords.XSDNS) return null; - XmlSchemaAnnotated typeNode; + XmlSchemaAnnotated? typeNode; if (_type == null || _type.Length == 0) { - _type = isAttr ? attr.RefName.Name : el.RefName.Name; + _type = isAttr ? attr!.RefName.Name : el!.RefName.Name; if (_type == null || _type.Length == 0) - typeNode = isAttr ? attr.SchemaType : el.SchemaType; + typeNode = isAttr ? attr!.SchemaType : el!.SchemaType; else - typeNode = isAttr ? FindTypeNode((XmlSchemaAnnotated)_attributes[attr.RefName]) : FindTypeNode((XmlSchemaAnnotated)_elementsTable[el.RefName]); + typeNode = isAttr ? FindTypeNode((XmlSchemaAnnotated)_attributes![attr!.RefName]!) : FindTypeNode((XmlSchemaAnnotated)_elementsTable![el!.RefName]!); } else - typeNode = (XmlSchemaAnnotated)_schemaTypes[isAttr ? ((XmlSchemaAttribute)node).SchemaTypeName : ((XmlSchemaElement)node).SchemaTypeName]; + typeNode = (XmlSchemaAnnotated?)_schemaTypes![isAttr ? ((XmlSchemaAttribute)node).SchemaTypeName : ((XmlSchemaElement)node).SchemaTypeName]; return typeNode; } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, string strType, DataTable table, bool isBase, XmlAttribute[] attrs, bool isNillable) + internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, string strType, DataTable table, bool isBase, XmlAttribute[]? attrs, bool isNillable) { // disallow multiple simple content columns for the table if (FromInference && table.XmlText != null) @@ -1967,8 +1964,8 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, return; } - Type type = null; - SimpleType xsdType = null; + Type? type = null; + SimpleType? xsdType = null; // if (typeNode.QualifiedName.Namespace != Keywords.XSDNS) { // this means UDSimpleType if (typeNode.QualifiedName.Name != null && typeNode.QualifiedName.Name.Length != 0 && typeNode.QualifiedName.Namespace != Keywords.XSDNS) @@ -1979,7 +1976,7 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, } else {// previous code V 1.1 - XmlSchemaSimpleType ancestor = typeNode.BaseXmlSchemaType as XmlSchemaSimpleType; + XmlSchemaSimpleType? ancestor = typeNode.BaseXmlSchemaType as XmlSchemaSimpleType; if ((ancestor != null) && (ancestor.QualifiedName.Namespace != Keywords.XSDNS)) { xsdType = new SimpleType(typeNode); @@ -1989,8 +1986,8 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, { rootType = rootType.BaseSimpleType; } - type = ParseDataType(rootType.BaseType); - strType = xsdType.Name; + type = ParseDataType(rootType.BaseType!); + strType = xsdType.Name!; } else { @@ -2018,7 +2015,7 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, bool isToAdd = true; if ((!isBase) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; } else @@ -2031,7 +2028,7 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, SetExtProperties(column, attrs); string tmp = (-1).ToString(CultureInfo.CurrentCulture); - string defValue = null; + string? defValue = null; //try to see if attributes contain allownull column.AllowDBNull = isNillable; @@ -2053,7 +2050,7 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } // Update XSD type to point to simple types actual namespace instead of normalized default namespace in case of remoting @@ -2096,7 +2093,7 @@ internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal void HandleSimpleContentColumn(string strType, DataTable table, bool isBase, XmlAttribute[] attrs, bool isNillable) + internal void HandleSimpleContentColumn(string strType, DataTable table, bool isBase, XmlAttribute[]? attrs, bool isNillable) { // for Named Simple type support : We should not recieved anything here other than string. // there can not be typed simple content @@ -2104,7 +2101,7 @@ internal void HandleSimpleContentColumn(string strType, DataTable table, bool is if (FromInference && table.XmlText != null) // backward compatability for inference return; - Type type = null; + Type? type = null; if (strType == null) { return; @@ -2131,7 +2128,7 @@ internal void HandleSimpleContentColumn(string strType, DataTable table, bool is if ((!isBase) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; } else @@ -2144,7 +2141,7 @@ internal void HandleSimpleContentColumn(string strType, DataTable table, bool is SetExtProperties(column, attrs); string tmp = (-1).ToString(CultureInfo.CurrentCulture); - string defValue = null; + string? defValue = null; //try to see if attributes contain allownull column.AllowDBNull = isNillable; @@ -2166,7 +2163,7 @@ internal void HandleSimpleContentColumn(string strType, DataTable table, bool is if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } column.XmlDataType = strType; @@ -2199,13 +2196,13 @@ internal void HandleSimpleContentColumn(string strType, DataTable table, bool is [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, bool isBase) { - Type type = null; - XmlSchemaAttribute attr = attrib.Name != null ? attrib : (XmlSchemaAttribute)_attributes[attrib.RefName]; + Type? type = null; + XmlSchemaAttribute? attr = attrib.Name != null ? attrib : (XmlSchemaAttribute)_attributes![attrib.RefName]!; - XmlSchemaAnnotated typeNode = FindTypeNode(attr); - string strType = null; - SimpleType xsdType = null; + XmlSchemaAnnotated? typeNode = FindTypeNode(attr); + string? strType = null; + SimpleType? xsdType = null; if (typeNode == null) { @@ -2225,7 +2222,7 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, } else if (typeNode is XmlSchemaSimpleType) { - XmlSchemaSimpleType node = typeNode as XmlSchemaSimpleType; + XmlSchemaSimpleType node = (typeNode as XmlSchemaSimpleType)!; xsdType = new SimpleType(node); if (node.QualifiedName.Name != null && node.QualifiedName.Name.Length != 0 && node.QualifiedName.Namespace != Keywords.XSDNS) { @@ -2235,7 +2232,7 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, } else { - type = ParseDataType(xsdType.BaseType); + type = ParseDataType(xsdType.BaseType!); strType = xsdType.Name; if (xsdType.Length == 1 && type == typeof(string)) { @@ -2262,7 +2259,7 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, if ((!isBase || FromInference) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; if (FromInference) @@ -2294,12 +2291,12 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } if (xsdType != null && xsdType.Name != null && xsdType.Name.Length > 0) { - if (XSDSchema.GetMsdataAttribute(typeNode, Keywords.TARGETNAMESPACE) != null) + if (XSDSchema.GetMsdataAttribute(typeNode!, Keywords.TARGETNAMESPACE) != null) { column.XmlDataType = xsdType.SimpleTypeQualifiedName; } @@ -2330,7 +2327,7 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, column.ColumnMapping = MappingType.Hidden; column.AllowDBNull = GetBooleanAttribute(attr, Keywords.MSD_ALLOWDBNULL, true); - string defValue = GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE); + string? defValue = GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE); if (defValue != null) try { @@ -2344,7 +2341,7 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, // XDR March change - string strDefault = (attrib.Use == XmlSchemaUse.Required) ? GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE) : attr.DefaultValue; + string? strDefault = (attrib.Use == XmlSchemaUse.Required) ? GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE) : attr.DefaultValue; if ((attr.Use == XmlSchemaUse.Optional) && (strDefault == null)) strDefault = attr.FixedValue; @@ -2362,15 +2359,15 @@ internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool isBase) { - Type type = null; - XmlSchemaElement el = elem.Name != null ? elem : (XmlSchemaElement)_elementsTable[elem.RefName]; + Type? type = null; + XmlSchemaElement? el = elem.Name != null ? elem : (XmlSchemaElement?)_elementsTable![elem.RefName]; if (el == null) // it's possible due to some XSD compiler optimizations return; // do nothing - XmlSchemaAnnotated typeNode = FindTypeNode(el); - string strType = null; - SimpleType xsdType = null; + XmlSchemaAnnotated? typeNode = FindTypeNode(el); + string? strType = null; + SimpleType? xsdType = null; if (typeNode == null) { @@ -2387,11 +2384,11 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i } else if (typeNode is XmlSchemaSimpleType) { - XmlSchemaSimpleType simpleTypeNode = typeNode as XmlSchemaSimpleType; - xsdType = new SimpleType(simpleTypeNode); + XmlSchemaSimpleType? simpleTypeNode = typeNode as XmlSchemaSimpleType; + xsdType = new SimpleType(simpleTypeNode!); // ((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name.Length != 0 check is for annonymos simple type, // it should be user defined Named simple type - if (((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name.Length != 0 && ((XmlSchemaSimpleType)typeNode).QualifiedName.Namespace != Keywords.XSDNS) + if (((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name!.Length != 0 && ((XmlSchemaSimpleType)typeNode).QualifiedName.Namespace != Keywords.XSDNS) { strType = ((XmlSchemaSimpleType)typeNode).QualifiedName.ToString(); // use qualifed name type = ParseDataType(strType); @@ -2399,17 +2396,17 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i else { simpleTypeNode = (xsdType.XmlBaseType != null && xsdType.XmlBaseType.Namespace != Keywords.XSDNS) ? - _schemaTypes[xsdType.XmlBaseType] as XmlSchemaSimpleType : + _schemaTypes![xsdType.XmlBaseType] as XmlSchemaSimpleType : null; while (simpleTypeNode != null) { xsdType.LoadTypeValues(simpleTypeNode); simpleTypeNode = (xsdType.XmlBaseType != null && xsdType.XmlBaseType.Namespace != Keywords.XSDNS) ? - _schemaTypes[xsdType.XmlBaseType] as XmlSchemaSimpleType : + _schemaTypes![xsdType.XmlBaseType] as XmlSchemaSimpleType : null; } - type = ParseDataType(xsdType.BaseType); + type = ParseDataType(xsdType.BaseType!); strType = xsdType.Name; if (xsdType.Length == 1 && type == typeof(string)) @@ -2448,7 +2445,7 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i if (((!isBase) || FromInference) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; if (FromInference) @@ -2479,13 +2476,13 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i if (!string.IsNullOrEmpty(column.Expression)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } // Update XSD type to point to simple types actual namespace instead of normalized default namespace in case of remoting if (xsdType != null && xsdType.Name != null && xsdType.Name.Length > 0) { - if (XSDSchema.GetMsdataAttribute(typeNode, Keywords.TARGETNAMESPACE) != null) + if (XSDSchema.GetMsdataAttribute(typeNode!, Keywords.TARGETNAMESPACE) != null) { column.XmlDataType = xsdType.SimpleTypeQualifiedName; } @@ -2512,7 +2509,7 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i } else if (elem.Form == XmlSchemaForm.None) { - XmlSchemaObject e = elem.Parent; + XmlSchemaObject e = elem.Parent!; while (e.Parent != null) { e = e.Parent; @@ -2548,7 +2545,7 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i column.Prefix = GetPrefix(column.Namespace); // it can inherit its NS from DataTable, if it is null } - string strDefault = el.DefaultValue; + string? strDefault = el.DefaultValue; if (strDefault != null) try { @@ -2563,22 +2560,22 @@ internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool i [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) { - string dsName = node.Name; + string? dsName = node.Name; string dsNamespace = node.QualifiedName.Namespace; List tableSequenceList = new List(); - string value = GetMsdataAttribute(node, Keywords.MSD_LOCALE); + string? value = GetMsdataAttribute(node, Keywords.MSD_LOCALE); if (null != value) { // set by user if (0 != value.Length) { // <... msdata:Locale="en-US"/> - _ds.Locale = new CultureInfo(value); + _ds!.Locale = new CultureInfo(value); } else { - _ds.Locale = CultureInfo.InvariantCulture; + _ds!.Locale = CultureInfo.InvariantCulture; } } else @@ -2586,12 +2583,12 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) // MSD_LOCALE overrides MSD_USECURRENTLOCALE if (GetBooleanAttribute(node, Keywords.MSD_USECURRENTLOCALE, false)) { - _ds.SetLocaleValue(CultureInfo.CurrentCulture, false); + _ds!.SetLocaleValue(CultureInfo.CurrentCulture, false); } else { // Everett behavior before <... msdata:UseCurrentLocale="true"/> - _ds.SetLocaleValue(new CultureInfo(0x409), false); + _ds!.SetLocaleValue(new CultureInfo(0x409), false); } } @@ -2621,10 +2618,10 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) if (FromInference) _ds.Prefix = GetPrefix(_ds.Namespace); - XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(node); + XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(node)!; if (ct.Particle != null) { - XmlSchemaObjectCollection items = GetParticleItems(ct.Particle); + XmlSchemaObjectCollection? items = GetParticleItems(ct.Particle); if (items == null) { @@ -2643,7 +2640,7 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) } else { - DataTable tempTable = _ds.Tables.GetTable(XmlConvert.DecodeName(GetInstanceName((XmlSchemaElement)el)), node.QualifiedName.Namespace); + DataTable? tempTable = _ds.Tables.GetTable(XmlConvert.DecodeName(GetInstanceName((XmlSchemaElement)el)), node.QualifiedName.Namespace); if (tempTable != null) { tableSequenceList.Add(tempTable); // if ref table is created, add it @@ -2661,14 +2658,14 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) } } - DataTable child = HandleTable((XmlSchemaElement)el); + DataTable? child = HandleTable((XmlSchemaElement)el); if (child != null) { child._fNestedInDataset = true; } if (FromInference) { - tableSequenceList.Add(child); + tableSequenceList.Add(child!); } } else if (el is XmlSchemaChoice) @@ -2685,7 +2682,7 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) if ((((XmlSchemaElement)choiceEl).RefName.Name.Length != 0) && (!FromInference && ((XmlSchemaElement)choiceEl).MaxOccurs != decimal.One && !(((XmlSchemaElement)choiceEl).SchemaType is XmlSchemaComplexType))) continue; - DataTable child = HandleTable((XmlSchemaElement)choiceEl); + DataTable child = HandleTable((XmlSchemaElement)choiceEl)!; if (FromInference) { tableSequenceList.Add(child); @@ -2705,7 +2702,7 @@ internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) { foreach (XmlSchemaIdentityConstraint key in node.Constraints) { - XmlSchemaKeyref keyref = key as XmlSchemaKeyref; + XmlSchemaKeyref? keyref = key as XmlSchemaKeyref; if (keyref == null) continue; @@ -2732,18 +2729,18 @@ private void AddTablesToList(List tableList, DataTable dt) if (!tableList.Contains(dt)) { tableList.Add(dt); - foreach (DataTable childTable in _tableDictionary[dt]) + foreach (DataTable childTable in _tableDictionary![dt]) { AddTablesToList(tableList, childTable); } } } - private string GetPrefix(string ns) + private string? GetPrefix(string ns) { if (ns == null) return null; - foreach (XmlSchema schemaRoot in _schemaSet.Schemas()) + foreach (XmlSchema schemaRoot in _schemaSet!.Schemas()) { XmlQualifiedName[] qualifiedNames = schemaRoot.Namespaces.ToArray(); for (int i = 0; i < qualifiedNames.Length; i++) @@ -2755,11 +2752,11 @@ private string GetPrefix(string ns) return null; } - private string GetNamespaceFromPrefix(string prefix) + private string? GetNamespaceFromPrefix(string? prefix) { if ((prefix == null) || (prefix.Length == 0)) return null; - foreach (XmlSchema schemaRoot in _schemaSet.Schemas()) + foreach (XmlSchema schemaRoot in _schemaSet!.Schemas()) { XmlQualifiedName[] qualifiedNames = schemaRoot.Namespaces.ToArray(); for (int i = 0; i < qualifiedNames.Length; i++) @@ -2772,9 +2769,9 @@ private string GetNamespaceFromPrefix(string prefix) } - private string GetTableNamespace(XmlSchemaIdentityConstraint key) + private string? GetTableNamespace(XmlSchemaIdentityConstraint key) { - string xpath = key.Selector.XPath; + string xpath = key.Selector!.XPath!; string[] split = xpath.Split('/'); string prefix = string.Empty; @@ -2795,7 +2792,7 @@ private string GetTableNamespace(XmlSchemaIdentityConstraint key) private string GetTableName(XmlSchemaIdentityConstraint key) { - string xpath = key.Selector.XPath; + string xpath = key.Selector!.XPath!; string[] split = xpath.Split('/', ':'); string tableName = split[split.Length - 1]; //get the last string after '/' and ':' @@ -2811,7 +2808,7 @@ internal bool IsTable(XmlSchemaElement node) if (node.MaxOccurs == decimal.Zero) return false; - XmlAttribute[] attribs = node.UnhandledAttributes; + XmlAttribute[]? attribs = node.UnhandledAttributes; if (attribs != null) { for (int i = 0; i < attribs.Length; i++) @@ -2824,7 +2821,7 @@ internal bool IsTable(XmlSchemaElement node) } } - object typeNode = FindTypeNode(node); + object? typeNode = FindTypeNode(node); if ((node.MaxOccurs > decimal.One) && typeNode == null) { @@ -2840,7 +2837,7 @@ internal bool IsTable(XmlSchemaElement node) XmlSchemaComplexType ctNode = (XmlSchemaComplexType)typeNode; if (ctNode.IsAbstract) - throw ExceptionBuilder.CannotInstantiateAbstract(node.Name); + throw ExceptionBuilder.CannotInstantiateAbstract(node.Name!); return true; } @@ -2849,19 +2846,19 @@ internal bool IsTable(XmlSchemaElement node) // return (elements.IndexOf(node) != -1); // } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal DataTable HandleTable(XmlSchemaElement node) + internal DataTable? HandleTable(XmlSchemaElement node) { if (!IsTable(node)) return null; - object typeNode = FindTypeNode(node); + object? typeNode = FindTypeNode(node); if ((node.MaxOccurs > decimal.One) && typeNode == null) { return InstantiateSimpleTable(node); } - DataTable table = InstantiateTable(node, (XmlSchemaComplexType)typeNode, (node.RefName != null)); // this is wrong , correct check should be node.RefName.IsEmpty + DataTable table = InstantiateTable(node, (XmlSchemaComplexType)typeNode!, (node.RefName != null)); // this is wrong , correct check should be node.RefName.IsEmpty table._fNestedInDataset = false; return table; diff --git a/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs b/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs index b5f5eb009801e..3f80ecdf1cbee 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Collections; using System.Collections.Generic; using System.Data.Common; @@ -18,17 +15,17 @@ namespace System.Data { internal sealed class XmlDataLoader { - private readonly DataSet _dataSet; - private XmlToDatasetMap _nodeToSchemaMap; + private readonly DataSet? _dataSet; + private XmlToDatasetMap? _nodeToSchemaMap; private readonly Hashtable _nodeToRowMap; - private readonly Stack _childRowsStack; + private readonly Stack? _childRowsStack; private readonly bool _fIsXdr; internal bool _isDiffgram; - private XmlElement _topMostNode; + private XmlElement? _topMostNode; private readonly bool _ignoreSchema; - private readonly DataTable _dataTable; + private readonly DataTable? _dataTable; private readonly bool _isTableLevel; private bool _fromInference; @@ -96,17 +93,17 @@ internal bool FromInference // after loading, all detached DataRows are attached to their tables [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private void AttachRows(DataRow parentRow, XmlNode parentElement) + private void AttachRows(DataRow? parentRow, XmlNode parentElement) { if (parentElement == null) return; - for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = parentElement.FirstChild; n != null; n = n.NextSibling) { if (n.NodeType == XmlNodeType.Element) { XmlElement e = (XmlElement)n; - DataRow r = GetRowFromElement(e); + DataRow? r = GetRowFromElement(e); if (r != null && r.RowState == DataRowState.Detached) { if (parentRow != null) @@ -129,7 +126,7 @@ private void AttachRows(DataRow parentRow, XmlNode parentElement) private int CountNonNSAttributes(XmlNode node) { int count = 0; - for (int i = 0; i < node.Attributes.Count; i++) + for (int i = 0; i < node.Attributes!.Count; i++) { if (!FExcludedNamespace(node.Attributes[i].NamespaceURI)) count++; @@ -137,9 +134,9 @@ private int CountNonNSAttributes(XmlNode node) return count; } - private string GetValueForTextOnlyColums(XmlNode n) + private string GetValueForTextOnlyColums(XmlNode? n) { - string value = null; + string? value = null; // don't consider whitespace while (n != null && (n.NodeType == XmlNodeType.Whitespace || !IsTextLikeNode(n.NodeType))) @@ -173,15 +170,15 @@ private string GetValueForTextOnlyColums(XmlNode n) return value; } - private string GetInitialTextFromNodes(ref XmlNode n) + private string GetInitialTextFromNodes(ref XmlNode? n) { - string value = null; + string? value = null; if (n != null) { // don't consider whitespace while (n.NodeType == XmlNodeType.Whitespace) - n = n.NextSibling; + n = n.NextSibling!; if (IsTextLikeNode(n.NodeType) && (n.NextSibling == null || !IsTextLikeNode(n.NodeType))) { @@ -207,7 +204,7 @@ private string GetInitialTextFromNodes(ref XmlNode n) return value; } - private DataColumn GetTextOnlyColumn(DataRow row) + private DataColumn? GetTextOnlyColumn(DataRow row) { DataColumnCollection columns = row.Table.Columns; int cCols = columns.Count; @@ -220,20 +217,20 @@ private DataColumn GetTextOnlyColumn(DataRow row) return null; } - internal DataRow GetRowFromElement(XmlElement e) + internal DataRow? GetRowFromElement(XmlElement e) { - return (DataRow)_nodeToRowMap[e]; + return (DataRow?)_nodeToRowMap[e]; } internal bool FColumnElement(XmlElement e) { - if (_nodeToSchemaMap.GetColumnSchema(e, FIgnoreNamespace(e)) == null) + if (_nodeToSchemaMap!.GetColumnSchema(e, FIgnoreNamespace(e)) == null) return false; if (CountNonNSAttributes(e) > 0) return false; - for (XmlNode tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) + for (XmlNode? tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) if (tabNode is XmlElement) return false; @@ -247,11 +244,11 @@ private bool FExcludedNamespace(string ns) private bool FIgnoreNamespace(XmlNode node) { - XmlNode ownerNode; + XmlNode? ownerNode; if (!_fIsXdr) return false; if (node is XmlAttribute) - ownerNode = ((XmlAttribute)node).OwnerElement; + ownerNode = ((XmlAttribute)node).OwnerElement!; else ownerNode = node; if (ownerNode.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal)) @@ -304,23 +301,23 @@ internal void LoadData(XmlDocument xdoc) if (_isTableLevel) { - saveEnforce = _dataTable.EnforceConstraints; + saveEnforce = _dataTable!.EnforceConstraints; _dataTable.EnforceConstraints = false; } else { - saveEnforce = _dataSet.EnforceConstraints; + saveEnforce = _dataSet!.EnforceConstraints; _dataSet.EnforceConstraints = false; _dataSet._fInReadXml = true; } if (_isTableLevel) { - _nodeToSchemaMap = new XmlToDatasetMap(_dataTable, xdoc.NameTable); + _nodeToSchemaMap = new XmlToDatasetMap(_dataTable!, xdoc.NameTable); } else { - _nodeToSchemaMap = new XmlToDatasetMap(_dataSet, xdoc.NameTable); + _nodeToSchemaMap = new XmlToDatasetMap(_dataSet!, xdoc.NameTable); } /* // Top level table or dataset ? @@ -341,11 +338,11 @@ internal void LoadData(XmlDocument xdoc) } } */ - DataRow topRow = null; + DataRow? topRow = null; if (_isTableLevel || (_dataSet != null && _dataSet._fTopLevelTable)) { XmlElement e = xdoc.DocumentElement; - DataTable topTable = (DataTable)_nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + DataTable? topTable = (DataTable?)_nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); if (topTable != null) { topRow = topTable.CreateEmptyRow(); //enzol perf @@ -363,11 +360,11 @@ internal void LoadData(XmlDocument xdoc) if (_isTableLevel) { - _dataTable.EnforceConstraints = saveEnforce; + _dataTable!.EnforceConstraints = saveEnforce; } else { - _dataSet._fInReadXml = false; + _dataSet!._fInReadXml = false; _dataSet.EnforceConstraints = saveEnforce; } } @@ -375,7 +372,7 @@ internal void LoadData(XmlDocument xdoc) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private void LoadRowData(DataRow row, XmlElement rowElement) { - XmlNode n; + XmlNode? n; DataTable table = row.Table; if (FromInference) table.Prefix = rowElement.Prefix; @@ -389,7 +386,7 @@ private void LoadRowData(DataRow row, XmlElement rowElement) n = rowElement.FirstChild; // Look for data to fill the TextOnly column - DataColumn column = GetTextOnlyColumn(row); + DataColumn? column = GetTextOnlyColumn(row); if (column != null) { foundColumns[column] = column; @@ -407,7 +404,7 @@ private void LoadRowData(DataRow row, XmlElement rowElement) { XmlElement e = (XmlElement)n; - object schema = _nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + object? schema = _nodeToSchemaMap!.GetSchemaForNode(e, FIgnoreNamespace(e)); if (schema is DataTable) { if (FColumnElement(e)) @@ -447,7 +444,7 @@ private void LoadRowData(DataRow row, XmlElement rowElement) } // if no more siblings, ascend back toward original element (rowElement) - while (n != rowElement && n.NextSibling == null) + while (n != rowElement && n!.NextSibling == null) { n = n.ParentNode; } @@ -461,7 +458,7 @@ private void LoadRowData(DataRow row, XmlElement rowElement) // foreach (XmlAttribute attr in rowElement.Attributes) { - object schema = _nodeToSchemaMap.GetColumnSchema(attr, FIgnoreNamespace(attr)); + object? schema = _nodeToSchemaMap!.GetColumnSchema(attr, FIgnoreNamespace(attr)); if (schema != null && schema is DataColumn) { DataColumn c = (DataColumn)schema; @@ -504,7 +501,7 @@ private void LoadRowData(DataRow row, XmlElement rowElement) // load all data from tree structre into datarows [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private void LoadRows(DataRow parentRow, XmlNode parentElement) + private void LoadRows(DataRow? parentRow, XmlNode parentElement) { if (parentElement == null) return; @@ -515,16 +512,16 @@ private void LoadRows(DataRow parentRow, XmlNode parentElement) parentElement.LocalName == Keywords.XDR_SCHEMA && parentElement.NamespaceURI == Keywords.XDRNS) return; - for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = parentElement.FirstChild; n != null; n = n.NextSibling) { if (n is XmlElement) { XmlElement e = (XmlElement)n; - object schema = _nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + object? schema = _nodeToSchemaMap!.GetSchemaForNode(e, FIgnoreNamespace(e)); if (schema != null && schema is DataTable) { - DataRow r = GetRowFromElement(e); + DataRow? r = GetRowFromElement(e); if (r == null) { // skip columns which has the same name as another table @@ -556,24 +553,24 @@ private void SetRowValueFromXmlText(DataRow row, DataColumn col, string xmlText) row[col] = col.ConvertXmlToObject(xmlText); } - private XmlReader _dataReader; - private object _XSD_XMLNS_NS; - private object _XDR_SCHEMA; - private object _XDRNS; - private object _SQL_SYNC; - private object _UPDGNS; - private object _XSD_SCHEMA; - private object _XSDNS; - - private object _DFFNS; - private object _MSDNS; - private object _DIFFID; - private object _HASCHANGES; - private object _ROWORDER; + private XmlReader? _dataReader; + private object? _XSD_XMLNS_NS; + private object? _XDR_SCHEMA; + private object? _XDRNS; + private object? _SQL_SYNC; + private object? _UPDGNS; + private object? _XSD_SCHEMA; + private object? _XSDNS; + + private object? _DFFNS; + private object? _MSDNS; + private object? _DIFFID; + private object? _HASCHANGES; + private object? _ROWORDER; private void InitNameTable() { - XmlNameTable nameTable = _dataReader.NameTable; + XmlNameTable nameTable = _dataReader!.NameTable; _XSD_XMLNS_NS = nameTable.Add(Keywords.XSD_XMLNS_NS); _XDR_SCHEMA = nameTable.Add(Keywords.XDR_SCHEMA); @@ -597,23 +594,23 @@ internal void LoadData(XmlReader reader) int entryDepth = _dataReader.Depth; // Store current XML element depth so we'll read // correct portion of the XML and no more - bool fEnforce = _isTableLevel ? _dataTable.EnforceConstraints : _dataSet.EnforceConstraints; + bool fEnforce = _isTableLevel ? _dataTable!.EnforceConstraints : _dataSet!.EnforceConstraints; // Keep constraints status for datataset/table InitNameTable(); // Adds DataSet namespaces to reader's nametable if (_nodeToSchemaMap == null) { // Create XML to dataset map - _nodeToSchemaMap = _isTableLevel ? new XmlToDatasetMap(_dataReader.NameTable, _dataTable) : - new XmlToDatasetMap(_dataReader.NameTable, _dataSet); + _nodeToSchemaMap = _isTableLevel ? new XmlToDatasetMap(_dataReader.NameTable, _dataTable!) : + new XmlToDatasetMap(_dataReader.NameTable, _dataSet!); } if (_isTableLevel) { - _dataTable.EnforceConstraints = false; // Disable constraints + _dataTable!.EnforceConstraints = false; // Disable constraints } else { - _dataSet.EnforceConstraints = false; // Disable constraints + _dataSet!.EnforceConstraints = false; // Disable constraints _dataSet._fInReadXml = true; // We're in ReadXml now } @@ -621,7 +618,7 @@ internal void LoadData(XmlReader reader) { // Do we have top node? if (!_isDiffgram && !_isTableLevel) { // Not a diffgram and not DataSet? - DataTable table = _nodeToSchemaMap.GetSchemaForNode(_topMostNode, FIgnoreNamespace(_topMostNode)) as DataTable; + DataTable? table = _nodeToSchemaMap.GetSchemaForNode(_topMostNode, FIgnoreNamespace(_topMostNode)) as DataTable; // Try to match table in the dataset to this node if (table != null) { // Got the table ? @@ -642,7 +639,7 @@ internal void LoadData(XmlReader reader) _dataReader.Read(); continue; } - DataTable table = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? table = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (table == null) { // Read till table is found @@ -658,11 +655,11 @@ internal void LoadData(XmlReader reader) if (_isTableLevel) { - _dataTable.EnforceConstraints = fEnforce; // Restore constraints and return + _dataTable!.EnforceConstraints = fEnforce; // Restore constraints and return } else { - _dataSet._fInReadXml = false; // We're done. + _dataSet!._fInReadXml = false; // We're done. _dataSet.EnforceConstraints = fEnforce; // Restore constraints and return } } @@ -701,35 +698,35 @@ private void LoadTopMostTable(DataTable table) Debug.Assert(_topMostNode != null, "topMostNode is null on LoadTopMostTable() entry"); Debug.Assert(!_isDiffgram, "Diffgram mode is on while we have topMostNode table. This is bad."); - bool topNodeIsTable = _isTableLevel || (_dataSet.DataSetName != table.TableName); + bool topNodeIsTable = _isTableLevel || (_dataSet!.DataSetName != table.TableName); // If table name we have matches dataset // name top node could be a DataSet OR a table. // It's a table overwise. - DataRow row = null; // Data row we're going to add to this table + DataRow? row = null; // Data row we're going to add to this table bool matchFound = false; // Assume we found no matching elements - int entryDepth = _dataReader.Depth - 1; // Store current reader depth so we know when to stop reading + int entryDepth = _dataReader!.Depth - 1; // Store current reader depth so we know when to stop reading // Adjust depth by one as we've read top most element // outside this method. string textNodeValue; // Value of a text node we might have Debug.Assert(entryDepth >= 0, "Wrong entry Depth for top most element."); - int entryChild = _childRowsStack.Count; // Memorize child stack level on entry + int entryChild = _childRowsStack!.Count; // Memorize child stack level on entry - DataColumn c; // Hold column here + DataColumn? c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; // This is the columns data we might find - XmlNode n; // Need this to pass by reference + XmlNode? n; // Need this to pass by reference foreach (XmlAttribute attr in _topMostNode.Attributes) { // Check all attributes in this node - c = _nodeToSchemaMap.GetColumnSchema(attr, FIgnoreNamespace(attr)) as DataColumn; + c = _nodeToSchemaMap!.GetColumnSchema(attr, FIgnoreNamespace(attr)) as DataColumn; // Try to match attribute to column if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) { @@ -752,7 +749,7 @@ private void LoadTopMostTable(DataTable table) switch (_dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element - object o = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? @@ -773,7 +770,7 @@ private void LoadTopMostTable(DataTable table) } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { // Do we have matched table in DataSet ? @@ -883,12 +880,12 @@ private void LoadTable(DataTable table, bool isNested) Debug.Assert(table != null, "Table to be loaded is null on LoadTable() entry"); - DataRow row = null; // Data row we're going to add to this table + DataRow? row = null; // Data row we're going to add to this table - int entryDepth = _dataReader.Depth; // Store current reader depth so we know when to stop reading - int entryChild = _childRowsStack.Count; // Memorize child stack level on entry + int entryDepth = _dataReader!.Depth; // Store current reader depth so we know when to stop reading + int entryChild = _childRowsStack!.Count; // Memorize child stack level on entry - DataColumn c; // Hold column here + DataColumn? c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; @@ -897,7 +894,7 @@ private void LoadTable(DataTable table, bool isNested) int rowOrder = -1; // Row to insert data to string diffId = string.Empty; // Diffgram ID string - string hasChanges = null; // Changes string + string? hasChanges = null; // Changes string bool hasErrors = false; // Set this in case of problem string textNodeValue; // Value of a text node we might have @@ -909,7 +906,7 @@ private void LoadTable(DataTable table, bool isNested) // Check all attributes one by one _dataReader.MoveToAttribute(i); // Get this attribute - c = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)) as DataColumn; + c = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)) as DataColumn; // Try to get column for this attribute if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) @@ -980,7 +977,7 @@ private void LoadTable(DataTable table, bool isNested) switch (_dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element - object o = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? @@ -1000,7 +997,7 @@ private void LoadTable(DataTable table, bool isNested) } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { // Do we have matched nested table in DataSet ? @@ -1019,7 +1016,7 @@ private void LoadTable(DataTable table, bool isNested) // but we'll try to load it so we could keep compatibility. // We won't try to match to columns as we have no idea // which table this potential column might belong to. - DataTable misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (misplacedTable != null) @@ -1153,9 +1150,9 @@ private void LoadColumn(DataColumn column, object[] foundColumns) // This is how .NET Framework works string text = string.Empty; // Column text. Assume empty string - string xsiNilString = null; // Possible NIL attribute string + string? xsiNilString = null; // Possible NIL attribute string - int entryDepth = _dataReader.Depth; // Store depth so we won't read too much + int entryDepth = _dataReader!.Depth; // Store depth so we won't read too much if (_dataReader.AttributeCount > 0) // If have attributes xsiNilString = _dataReader.GetAttribute(Keywords.XSI_NIL, Keywords.XSINS); @@ -1163,12 +1160,12 @@ private void LoadColumn(DataColumn column, object[] foundColumns) // We have to do it before we move to the next element if (column.IsCustomType) { // Custom type column - object columnValue = null; // Column value we're after. Assume no value. + object? columnValue = null; // Column value we're after. Assume no value. - string xsiTypeString = null; // XSI type name from TYPE attribute - string typeName = null; // Type name from MSD_INSTANCETYPE attribute + string? xsiTypeString = null; // XSI type name from TYPE attribute + string? typeName = null; // Type name from MSD_INSTANCETYPE attribute - XmlRootAttribute xmlAttrib = null; // Might need this attribute for XmlSerializer + XmlRootAttribute? xmlAttrib = null; // Might need this attribute for XmlSerializer if (_dataReader.AttributeCount > 0) { // If have attributes, get attributes we'll need @@ -1209,7 +1206,7 @@ private void LoadColumn(DataColumn column, object[] foundColumns) { // No NIL attribute. Get value bool skipped = false; - if (column.Table.DataSet != null && column.Table.DataSet._udtIsWrapped) + if (column.Table!.DataSet != null && column.Table.DataSet._udtIsWrapped) { _dataReader.Read(); // if UDT is wrapped, skip the wrapper skipped = true; @@ -1257,7 +1254,7 @@ private void LoadColumn(DataColumn column, object[] foundColumns) text = _dataReader.Value; // Get value. // See if we have other text nodes near. In most cases this loop will not be executed. - StringBuilder builder = null; + StringBuilder? builder = null; while (_dataReader.Read() && entryDepth < _dataReader.Depth && IsTextLikeNode(_dataReader.NodeType)) { if (builder == null) @@ -1291,9 +1288,9 @@ private void LoadColumn(DataColumn column, object[] foundColumns) // We've got element which is not supposed to he here. // That might be table which was misplaced. // Or it might be a column inside column (also misplaced). - object o = _nodeToSchemaMap.GetColumnSchema(column.Table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(column.Table!, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element - DataColumn c = o as DataColumn; // Perhaps, it's a column? + DataColumn? c = o as DataColumn; // Perhaps, it's a column? if (c != null) { // Do we have matched column in this table? @@ -1312,7 +1309,7 @@ private void LoadColumn(DataColumn column, object[] foundColumns) } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { @@ -1324,7 +1321,7 @@ private void LoadColumn(DataColumn column, object[] foundColumns) { // Not a nested column nor nested table. // Let's try other tables in the DataSet - DataTable misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (misplacedTable != null) { @@ -1370,7 +1367,7 @@ private void LoadColumn(DataColumn column, object[] foundColumns) [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private bool ProcessXsdSchema() { - if (((object)_dataReader.LocalName == _XSD_SCHEMA && (object)_dataReader.NamespaceURI == _XSDNS)) + if (((object)_dataReader!.LocalName == _XSD_SCHEMA && (object)_dataReader.NamespaceURI == _XSDNS)) { // Found XSD schema if (_ignoreSchema) @@ -1381,12 +1378,12 @@ private bool ProcessXsdSchema() { // Have to load schema. if (_isTableLevel) { // Loading into the DataTable ? - _dataTable.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a table + _dataTable!.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a table _nodeToSchemaMap = new XmlToDatasetMap(_dataReader.NameTable, _dataTable); } // Rebuild XML to DataSet map with new schema. else { // Loading into the DataSet ? - _dataSet.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a DataSet + _dataSet!.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a DataSet _nodeToSchemaMap = new XmlToDatasetMap(_dataReader.NameTable, _dataSet); } // Rebuild XML to DataSet map with new schema. } diff --git a/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs b/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs index f74bf3799fabd..1c042b2102993 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Xml; using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -18,8 +16,8 @@ internal sealed class XmlToDatasetMap private sealed class XmlNodeIdentety { public string LocalName; - public string NamespaceURI; - public XmlNodeIdentety(string localName, string namespaceURI) + public string? NamespaceURI; + public XmlNodeIdentety(string localName, string? namespaceURI) { LocalName = localName; NamespaceURI = namespaceURI; @@ -28,9 +26,9 @@ public override int GetHashCode() { return LocalName.GetHashCode(); } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { - XmlNodeIdentety id = (XmlNodeIdentety)obj; + XmlNodeIdentety id = (XmlNodeIdentety)obj!; return ( (string.Equals(LocalName, id.LocalName, StringComparison.OrdinalIgnoreCase)) && (string.Equals(NamespaceURI, id.NamespaceURI, StringComparison.OrdinalIgnoreCase)) @@ -46,7 +44,7 @@ internal sealed class XmlNodeIdHashtable : Hashtable public XmlNodeIdHashtable(int capacity) : base(capacity) { } - public object this[XmlNode node] + public object? this[XmlNode node] { get { @@ -56,7 +54,7 @@ public object this[XmlNode node] } } - public object this[XmlReader dataReader] + public object? this[XmlReader dataReader] { get { @@ -66,7 +64,7 @@ public object this[XmlReader dataReader] } } - public object this[DataTable table] + public object? this[DataTable table] { get { @@ -76,7 +74,7 @@ public object this[DataTable table] } } - public object this[string name] + public object? this[string name] { get { @@ -100,7 +98,7 @@ public TableSchemaInfo(DataTable tableSchema) private XmlNodeIdHashtable _tableSchemaMap; // Holds all the tables information - private TableSchemaInfo _lastTableSchemaInfo; + private TableSchemaInfo? _lastTableSchemaInfo; // Used to infer schema @@ -144,7 +142,7 @@ internal static bool IsMappedColumn(DataColumn c) // Used to infere schema - private TableSchemaInfo AddTableSchema(DataTable table, XmlNameTable nameTable) + private TableSchemaInfo? AddTableSchema(DataTable table, XmlNameTable nameTable) { // SDUB: Because in our case reader already read the document all names that we can meet in the // document already has an entry in NameTable. @@ -153,8 +151,8 @@ private TableSchemaInfo AddTableSchema(DataTable table, XmlNameTable nameTable) // First case deals with decoded names; Second one with encoded names. // We decided encoded names in first case (instead of decoding them in second) // because it save us time in LoadRows(). We have, as usual, more data them schemas - string tableLocalName = nameTable.Get(table.EncodedTableName); - string tableNamespace = nameTable.Get(table.Namespace); + string? tableLocalName = nameTable.Get(table.EncodedTableName); + string? tableNamespace = nameTable.Get(table.Namespace); if (tableLocalName == null) { // because name of this table isn't present in XML we don't need mapping for it. @@ -178,7 +176,7 @@ private TableSchemaInfo AddTableSchema(XmlNameTable nameTable, DataTable table) string _tableLocalName = table.EncodedTableName; // Table name - string tableLocalName = nameTable.Get(_tableLocalName); // Look it up in nametable + string? tableLocalName = nameTable.Get(_tableLocalName); // Look it up in nametable if (tableLocalName == null) { // If not found @@ -187,7 +185,7 @@ private TableSchemaInfo AddTableSchema(XmlNameTable nameTable, DataTable table) table._encodedTableName = tableLocalName; // And set it back - string tableNamespace = nameTable.Get(table.Namespace); // Look ip table namespace + string? tableNamespace = nameTable.Get(table.Namespace); // Look ip table namespace if (tableNamespace == null) { // If not found @@ -211,8 +209,8 @@ private TableSchemaInfo AddTableSchema(XmlNameTable nameTable, DataTable table) private bool AddColumnSchema(DataColumn col, XmlNameTable nameTable, XmlNodeIdHashtable columns) { - string columnLocalName = nameTable.Get(col.EncodedColumnName); - string columnNamespace = nameTable.Get(col.Namespace); + string? columnLocalName = nameTable.Get(col.EncodedColumnName); + string? columnNamespace = nameTable.Get(col.Namespace); if (columnLocalName == null) { return false; @@ -233,7 +231,7 @@ private bool AddColumnSchema(DataColumn col, XmlNameTable nameTable, XmlNodeIdHa private bool AddColumnSchema(XmlNameTable nameTable, DataColumn col, XmlNodeIdHashtable columns) { string _columnLocalName = XmlConvert.EncodeLocalName(col.ColumnName); - string columnLocalName = nameTable.Get(_columnLocalName); // Look it up in a name table + string? columnLocalName = nameTable.Get(_columnLocalName); // Look it up in a name table if (columnLocalName == null) { // Not found? @@ -242,7 +240,7 @@ private bool AddColumnSchema(XmlNameTable nameTable, DataColumn col, XmlNodeIdHa col._encodedColumnName = columnLocalName; // And set it back - string columnNamespace = nameTable.Get(col.Namespace); // Get column namespace from nametable + string? columnNamespace = nameTable.Get(col.Namespace); // Get column namespace from nametable if (columnNamespace == null) { // Not found ? @@ -266,13 +264,14 @@ private bool AddColumnSchema(XmlNameTable nameTable, DataColumn col, XmlNodeIdHa return true; } + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(DataSet dataSet, XmlNameTable nameTable) { _tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count); foreach (DataTable t in dataSet.Tables) { - TableSchemaInfo tableSchemaInfo = AddTableSchema(t, nameTable); + TableSchemaInfo? tableSchemaInfo = AddTableSchema(t, nameTable); if (tableSchemaInfo != null) { foreach (DataColumn c in t.Columns) @@ -288,7 +287,7 @@ private void BuildIdentityMap(DataSet dataSet, XmlNameTable nameTable) } // This one is used while reading data with preloaded schema - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) { _tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count); @@ -298,7 +297,7 @@ private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) // Hash tables with columns schema maps // and child tables schema maps - string dsNamespace = nameTable.Get(dataSet.Namespace); // Attept to look up DataSet namespace + string? dsNamespace = nameTable.Get(dataSet.Namespace); // Attept to look up DataSet namespace // in the name table if (dsNamespace == null) @@ -335,14 +334,14 @@ private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) // Handle namespaces and names as usuall string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName); - string tableLocalName = nameTable.Get(_tableLocalName); + string? tableLocalName = nameTable.Get(_tableLocalName); if (tableLocalName == null) { tableLocalName = nameTable.Add(_tableLocalName); } - string tableNamespace = nameTable.Get(r.ChildTable.Namespace); + string? tableNamespace = nameTable.Get(r.ChildTable.Namespace); if (tableNamespace == null) { @@ -358,12 +357,12 @@ private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) } // Used for inference - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(DataTable dataTable, XmlNameTable nameTable) { _tableSchemaMap = new XmlNodeIdHashtable(1); - TableSchemaInfo tableSchemaInfo = AddTableSchema(dataTable, nameTable); + TableSchemaInfo? tableSchemaInfo = AddTableSchema(dataTable, nameTable); if (tableSchemaInfo != null) { foreach (DataColumn c in dataTable.Columns) @@ -378,7 +377,7 @@ private void BuildIdentityMap(DataTable dataTable, XmlNameTable nameTable) } // This one is used while reading data with preloaded schema - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(XmlNameTable nameTable, DataTable dataTable) { ArrayList tableList = GetSelfAndDescendants(dataTable); // Get list of tables we're loading @@ -413,14 +412,14 @@ private void BuildIdentityMap(XmlNameTable nameTable, DataTable dataTable) // Handle namespaces and names as usuall string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName); - string tableLocalName = nameTable.Get(_tableLocalName); + string? tableLocalName = nameTable.Get(_tableLocalName); if (tableLocalName == null) { tableLocalName = nameTable.Add(_tableLocalName); } - string tableNamespace = nameTable.Get(r.ChildTable.Namespace); + string? tableNamespace = nameTable.Get(r.ChildTable.Namespace); if (tableNamespace == null) { @@ -443,7 +442,7 @@ private ArrayList GetSelfAndDescendants(DataTable dt) while (nCounter < tableList.Count) { - foreach (DataRelation childRelations in ((DataTable)tableList[nCounter]).ChildRelations) + foreach (DataRelation childRelations in ((DataTable)tableList[nCounter]!).ChildRelations) { if (!tableList.Contains(childRelations.ChildTable)) tableList.Add(childRelations.ChildTable); @@ -455,12 +454,12 @@ private ArrayList GetSelfAndDescendants(DataTable dt) } // Used to infer schema and top most node - public object GetColumnSchema(XmlNode node, bool fIgnoreNamespace) + public object? GetColumnSchema(XmlNode node, bool fIgnoreNamespace) { Debug.Assert(node != null, "Argument validation"); - TableSchemaInfo tableSchemaInfo = null; + TableSchemaInfo? tableSchemaInfo = null; - XmlNode nodeRegion = (node.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)node).OwnerElement : node.ParentNode; + XmlNode? nodeRegion = (node.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)node).OwnerElement : node.ParentNode; do { @@ -468,7 +467,7 @@ public object GetColumnSchema(XmlNode node, bool fIgnoreNamespace) { return null; } - tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[nodeRegion.LocalName] : _tableSchemaMap[nodeRegion]); + tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[nodeRegion.LocalName] : _tableSchemaMap[nodeRegion]); nodeRegion = nodeRegion.ParentNode; } while (tableSchemaInfo == null); @@ -480,11 +479,11 @@ public object GetColumnSchema(XmlNode node, bool fIgnoreNamespace) } - public object GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnoreNamespace) + public object? GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnoreNamespace) { if ((_lastTableSchemaInfo == null) || (_lastTableSchemaInfo.TableSchema != table)) { - _lastTableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[table.EncodedTableName] : _tableSchemaMap[table]); + _lastTableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[table.EncodedTableName]! : _tableSchemaMap[table]!); } if (fIgnoreNamespace) @@ -494,13 +493,13 @@ public object GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnor // Used to infer schema - public object GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) + public object? GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) { - TableSchemaInfo tableSchemaInfo = null; + TableSchemaInfo? tableSchemaInfo = null; if (node.NodeType == XmlNodeType.Element) { // If element - tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); + tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); } // Look up table schema info for it if (tableSchemaInfo != null) @@ -511,9 +510,9 @@ public object GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) return GetColumnSchema(node, fIgnoreNamespace); // Attempt to locate column } - public DataTable GetTableForNode(XmlReader node, bool fIgnoreNamespace) + public DataTable? GetTableForNode(XmlReader node, bool fIgnoreNamespace) { - TableSchemaInfo tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); + TableSchemaInfo? tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); if (tableSchemaInfo != null) { _lastTableSchemaInfo = tableSchemaInfo; @@ -543,7 +542,7 @@ private void HandleSpecialColumn(DataColumn col, XmlNameTable nameTable, XmlNode { nameTable.Add(tempColumnName); } - string columnNamespace = nameTable.Get(col.Namespace); + string? columnNamespace = nameTable.Get(col.Namespace); XmlNodeIdentety idColumn = new XmlNodeIdentety(tempColumnName, columnNamespace); columns[idColumn] = col; } From 9da4d075ef8c581a28c8c171f36ccb05b66f2346 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 9 Jul 2021 17:07:16 -0700 Subject: [PATCH 48/72] Remove Uri scheme validation from HttpRequestMessage (#55035) * Remove Uri scheme validation from HttpRequestMessage * PR feedback Move HttpUtilities to SocketsHttpHandler * Add request scheme check to WinHttpHandler * Skip .NET 6 specific WinHttpHandler test on Framework * Update InteropServices.JavaScript HttpRequestMessage test * Guard HttpMessageInvoker from calling HttpTelemetry with invalid Uris * PR feedback --- .../System/Net/Http/HttpClientHandlerTest.cs | 18 ++++ .../src/Resources/Strings.resx | 6 ++ .../src/System/Net/Http/WinHttpHandler.cs | 11 +++ .../src/Resources/Strings.resx | 9 +- .../src/System.Net.Http.csproj | 4 +- .../HttpUtilities.Browser.cs | 22 ----- .../src/System/Net/Http/HttpClient.cs | 31 ++---- .../src/System/Net/Http/HttpMessageInvoker.cs | 16 ++-- .../src/System/Net/Http/HttpRequestMessage.cs | 63 +++--------- .../System/Net/Http/HttpResponseMessage.cs | 7 +- .../src/System/Net/Http/HttpTelemetry.cs | 2 +- .../System/Net/Http/HttpUtilities.AnyOS.cs | 18 ---- .../Net/Http/MessageProcessingHandler.cs | 4 +- .../{ => SocketsHttpHandler}/HttpUtilities.cs | 32 +------ .../SocketsHttpHandler/SocketsHttpHandler.cs | 14 ++- .../tests/FunctionalTests/HttpClientTest.cs | 12 ++- .../FunctionalTests/HttpRequestMessageTest.cs | 19 +++- .../FunctionalTests/SocketsHttpHandlerTest.cs | 96 +++++++++++++++++++ .../System.Net.Http.Unit.Tests.csproj | 6 +- .../JavaScript/Http/HttpRequestMessageTest.cs | 16 +--- 20 files changed, 218 insertions(+), 188 deletions(-) delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs rename src/libraries/System.Net.Http/src/System/Net/Http/{ => SocketsHttpHandler}/HttpUtilities.cs (50%) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 98c23bdcde0c0..54497e28618f7 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -1950,5 +1950,23 @@ public async Task GetAsync_InvalidUrl_ExpectedExceptionThrown() await Assert.ThrowsAsync(() => client.GetStringAsync(invalidUri)); } } + + // HttpRequestMessage ctor guards against such Uris before .NET 6. We allow passing relative/unknown Uris to BrowserHttpHandler. + public static bool InvalidRequestUriTest_IsSupported => PlatformDetection.IsNotNetFramework && PlatformDetection.IsNotBrowser; + + [ConditionalFact(nameof(InvalidRequestUriTest_IsSupported))] + public async Task SendAsync_InvalidRequestUri_Throws() + { + using var invoker = new HttpMessageInvoker(CreateHttpClientHandler()); + + var request = new HttpRequestMessage(HttpMethod.Get, (Uri)null); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + + request = new HttpRequestMessage(HttpMethod.Get, new Uri("/relative", UriKind.Relative)); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + + request = new HttpRequestMessage(HttpMethod.Get, new Uri("foo://foo.bar")); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + } } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx index 3f4beab8dd846..4fcb089cc506d 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx @@ -126,4 +126,10 @@ WinHttpHandler is only supported on .NET Framework and .NET runtimes on Windows. It is not supported for Windows Store Applications (UWP) or Unix platforms. + + The '{0}' scheme is not supported. + + + An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. + diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 63bd0aaf92f36..9ce26b2716d24 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -578,6 +578,17 @@ protected override Task SendAsync( throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } + Uri? requestUri = request.RequestUri; + if (requestUri is null || !requestUri.IsAbsoluteUri) + { + throw new InvalidOperationException(SR.net_http_client_invalid_requesturi); + } + + if (requestUri.Scheme != Uri.UriSchemeHttp && requestUri.Scheme != Uri.UriSchemeHttps) + { + throw new NotSupportedException(SR.Format(SR.net_http_unsupported_requesturi_scheme, requestUri.Scheme)); + } + // Check for invalid combinations of properties. if (_proxy != null && _windowsProxyUsePolicy != WindowsProxyUsePolicy.UseCustomProxy) { diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 35ce6f88feb33..03081881762ef 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -196,13 +196,10 @@ The base address must be an absolute URI. - An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set. + An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. - - Only 'http' and 'https' schemes are allowed. - - - Only 'http', 'https', and 'blob' schemes are allowed. + + The '{0}' scheme is not supported. Value '{0}' is not a valid Base64 string. Error: {1} diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index bea0e12f9fe00..49bc41d756e4b 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -53,7 +53,6 @@ - @@ -180,6 +179,7 @@ + @@ -188,7 +188,6 @@ - @@ -623,7 +622,6 @@ - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs deleted file mode 100644 index 6e9b4b027fefe..0000000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - -namespace System.Net.Http -{ - internal static partial class HttpUtilities - { - internal static bool IsSupportedNonSecureScheme(string scheme) => - string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) - || IsBlobScheme(scheme) - || IsNonSecureWebSocketScheme(scheme); - - internal static bool IsBlobScheme(string scheme) => - string.Equals(scheme, "blob", StringComparison.OrdinalIgnoreCase); - - internal static string InvalidUriMessage => SR.net_http_client_http_browser_baseaddress_required; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs index 514744fee99d5..27b0c357cc686 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs @@ -26,8 +26,8 @@ public partial class HttpClient : HttpMessageInvoker private CancellationTokenSource _pendingRequestsCts; private HttpRequestHeaders? _defaultRequestHeaders; - private Version _defaultRequestVersion = HttpUtilities.DefaultRequestVersion; - private HttpVersionPolicy _defaultVersionPolicy = HttpUtilities.DefaultVersionPolicy; + private Version _defaultRequestVersion = HttpRequestMessage.DefaultRequestVersion; + private HttpVersionPolicy _defaultVersionPolicy = HttpRequestMessage.DefaultVersionPolicy; private Uri? _baseAddress; private TimeSpan _timeout; @@ -78,7 +78,12 @@ public Uri? BaseAddress get => _baseAddress; set { - CheckBaseAddress(value, nameof(value)); + // It's OK to not have a base address specified, but if one is, it needs to be absolute. + if (value is not null && !value.IsAbsoluteUri) + { + throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, nameof(value)); + } + CheckDisposedOrStarted(); if (NetEventSource.Log.IsEnabled()) NetEventSource.UriBaseAddress(this, value); @@ -621,7 +626,7 @@ private void HandleFailure(Exception e, bool telemetryStarted, HttpResponseMessa private static bool StartSend(HttpRequestMessage request) { - if (HttpTelemetry.Log.IsEnabled() && request.RequestUri != null) + if (HttpTelemetry.Log.IsEnabled()) { HttpTelemetry.Log.RequestStart(request); return true; @@ -810,24 +815,6 @@ private void PrepareRequestMessage(HttpRequestMessage request) return (pendingRequestsCts, DisposeTokenSource: false, pendingRequestsCts); } - private static void CheckBaseAddress(Uri? baseAddress, string parameterName) - { - if (baseAddress == null) - { - return; // It's OK to not have a base address specified. - } - - if (!baseAddress.IsAbsoluteUri) - { - throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, parameterName); - } - - if (!HttpUtilities.IsHttpUri(baseAddress)) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, parameterName); - } - } - private static bool IsNativeHandlerEnabled() { if (!AppContext.TryGetSwitch("System.Net.Http.UseNativeHttpHandler", out bool isEnabled)) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs index 4988469f1ec03..9b33c75b4b22d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs @@ -34,8 +34,7 @@ public HttpMessageInvoker(HttpMessageHandler handler, bool disposeHandler) } [UnsupportedOSPlatformAttribute("browser")] - public virtual HttpResponseMessage Send(HttpRequestMessage request, - CancellationToken cancellationToken) + public virtual HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { @@ -43,7 +42,7 @@ public virtual HttpResponseMessage Send(HttpRequestMessage request, } CheckDisposed(); - if (HttpTelemetry.Log.IsEnabled() && !request.WasSentByHttpClient() && request.RequestUri != null) + if (ShouldSendWithTelemetry(request)) { HttpTelemetry.Log.RequestStart(request); @@ -67,8 +66,7 @@ public virtual HttpResponseMessage Send(HttpRequestMessage request, } } - public virtual Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + public virtual Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { @@ -76,7 +74,7 @@ public virtual Task SendAsync(HttpRequestMessage request, } CheckDisposed(); - if (HttpTelemetry.Log.IsEnabled() && !request.WasSentByHttpClient() && request.RequestUri != null) + if (ShouldSendWithTelemetry(request)) { return SendAsyncWithTelemetry(_handler, request, cancellationToken); } @@ -103,6 +101,12 @@ static async Task SendAsyncWithTelemetry(HttpMessageHandler } } + private static bool ShouldSendWithTelemetry(HttpRequestMessage request) => + HttpTelemetry.Log.IsEnabled() && + !request.WasSentByHttpClient() && + request.RequestUri is Uri requestUri && + requestUri.IsAbsoluteUri; + internal static bool LogRequestFailed(bool telemetryStarted) { if (HttpTelemetry.Log.IsEnabled() && telemetryStarted) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs index eaa59cd022a88..d0c88d60f7714 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs @@ -1,17 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Collections.Generic; -using System.Diagnostics; namespace System.Net.Http { public class HttpRequestMessage : IDisposable { + internal static Version DefaultRequestVersion => HttpVersion.Version11; + internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower; + private const int MessageNotYetSent = 0; private const int MessageAlreadySent = 1; @@ -101,29 +102,12 @@ public Uri? RequestUri get { return _requestUri; } set { - if ((value != null) && (value.IsAbsoluteUri) && (!HttpUtilities.IsHttpUri(value))) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, nameof(value)); - } CheckDisposed(); - - // It's OK to set 'null'. HttpClient will add the 'BaseAddress'. If there is no 'BaseAddress' - // sending this message will throw. _requestUri = value; } } - public HttpRequestHeaders Headers - { - get - { - if (_headers == null) - { - _headers = new HttpRequestHeaders(); - } - return _headers; - } - } + public HttpRequestHeaders Headers => _headers ??= new HttpRequestHeaders(); internal bool HasHeaders => _headers != null; @@ -139,22 +123,18 @@ public HttpRequestMessage() public HttpRequestMessage(HttpMethod method, Uri? requestUri) { - InitializeValues(method, requestUri); + // It's OK to have a 'null' request Uri. If HttpClient is used, the 'BaseAddress' will be added. + // If there is no 'BaseAddress', sending this request message will throw. + // Note that we also allow the string to be empty: null and empty are considered equivalent. + _method = method ?? throw new ArgumentNullException(nameof(method)); + _requestUri = requestUri; + _version = DefaultRequestVersion; + _versionPolicy = DefaultVersionPolicy; } public HttpRequestMessage(HttpMethod method, string? requestUri) + : this(method, string.IsNullOrEmpty(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute)) { - // It's OK to have a 'null' request Uri. If HttpClient is used, the 'BaseAddress' will be added. - // If there is no 'BaseAddress', sending this request message will throw. - // Note that we also allow the string to be empty: null and empty are considered equivalent. - if (string.IsNullOrEmpty(requestUri)) - { - InitializeValues(method, null); - } - else - { - InitializeValues(method, new Uri(requestUri, UriKind.RelativeOrAbsolute)); - } } public override string ToString() @@ -179,25 +159,6 @@ public override string ToString() return sb.ToString(); } - [MemberNotNull(nameof(_method))] - [MemberNotNull(nameof(_version))] - private void InitializeValues(HttpMethod method, Uri? requestUri) - { - if (method is null) - { - throw new ArgumentNullException(nameof(method)); - } - if ((requestUri != null) && (requestUri.IsAbsoluteUri) && (!HttpUtilities.IsHttpUri(requestUri))) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, nameof(requestUri)); - } - - _method = method; - _requestUri = requestUri; - _version = HttpUtilities.DefaultRequestVersion; - _versionPolicy = HttpUtilities.DefaultVersionPolicy; - } - internal bool MarkAsSent() { return Interlocked.Exchange(ref _sendStatus, MessageAlreadySent) == MessageNotYetSent; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs index 6af856e3585f7..49f6a6f34a72e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs @@ -10,7 +10,8 @@ namespace System.Net.Http { public class HttpResponseMessage : IDisposable { - private const HttpStatusCode defaultStatusCode = HttpStatusCode.OK; + private const HttpStatusCode DefaultStatusCode = HttpStatusCode.OK; + private static Version DefaultResponseVersion => HttpVersion.Version11; private HttpStatusCode _statusCode; private HttpResponseHeaders? _headers; @@ -149,7 +150,7 @@ public bool IsSuccessStatusCode } public HttpResponseMessage() - : this(defaultStatusCode) + : this(DefaultStatusCode) { } @@ -161,7 +162,7 @@ public HttpResponseMessage(HttpStatusCode statusCode) } _statusCode = statusCode; - _version = HttpUtilities.DefaultResponseVersion; + _version = DefaultResponseVersion; } public HttpResponseMessage EnsureSuccessStatusCode() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs index a008637271e1a..ae52766e08116 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs @@ -36,7 +36,7 @@ private void RequestStart(string scheme, string host, int port, string pathAndQu [NonEvent] public void RequestStart(HttpRequestMessage request) { - Debug.Assert(request.RequestUri != null); + Debug.Assert(request.RequestUri != null && request.RequestUri.IsAbsoluteUri); RequestStart( request.RequestUri.Scheme, diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs deleted file mode 100644 index afa2d7d855133..0000000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - -namespace System.Net.Http -{ - internal static partial class HttpUtilities - { - internal static bool IsSupportedNonSecureScheme(string scheme) => - string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) - || IsNonSecureWebSocketScheme(scheme); - - internal static string InvalidUriMessage => SR.net_http_client_http_baseaddress_required; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs index f903186995e70..d0c3748d4943d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs @@ -63,7 +63,7 @@ protected internal sealed override Task SendAsync(HttpReque // We schedule a continuation task once the inner handler completes in order to trigger the response // processing method. ProcessResponse() is only called if the task wasn't canceled before. - sendAsyncTask.ContinueWithStandard(tcs, static (task, state) => + sendAsyncTask.ContinueWith(static (task, state) => { var sendState = (SendState)state!; MessageProcessingHandler self = sendState._handler; @@ -106,7 +106,7 @@ protected internal sealed override Task SendAsync(HttpReque // if the operation was canceled: We'll set the Task returned to the user to canceled. Passing the // cancellation token here would result in the continuation task to not be called at all. I.e. we // would never complete the task returned to the caller of SendAsync(). - }); + }, tcs, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } catch (OperationCanceledException e) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs similarity index 50% rename from src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs rename to src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs index 8c9d786fd49d2..1c2f442d3e21a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs @@ -1,30 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - namespace System.Net.Http { - internal static partial class HttpUtilities + internal static class HttpUtilities { - internal static Version DefaultRequestVersion => HttpVersion.Version11; - - internal static Version DefaultResponseVersion => HttpVersion.Version11; - - internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower; - - internal static bool IsHttpUri(Uri uri) - { - Debug.Assert(uri != null); - return IsSupportedScheme(uri.Scheme); - } - internal static bool IsSupportedScheme(string scheme) => IsSupportedNonSecureScheme(scheme) || IsSupportedSecureScheme(scheme); + internal static bool IsSupportedNonSecureScheme(string scheme) => + string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) || IsNonSecureWebSocketScheme(scheme); + internal static bool IsSupportedSecureScheme(string scheme) => string.Equals(scheme, "https", StringComparison.OrdinalIgnoreCase) || IsSecureWebSocketScheme(scheme); @@ -41,16 +28,5 @@ internal static bool IsSocksScheme(string scheme) => string.Equals(scheme, "socks5", StringComparison.OrdinalIgnoreCase) || string.Equals(scheme, "socks4a", StringComparison.OrdinalIgnoreCase) || string.Equals(scheme, "socks4", StringComparison.OrdinalIgnoreCase); - - // Always specify TaskScheduler.Default to prevent us from using a user defined TaskScheduler.Current. - // - // Since we're not doing any CPU and/or I/O intensive operations, continue on the same thread. - // This results in better performance since the continuation task doesn't get scheduled by the - // scheduler and there are no context switches required. - internal static Task ContinueWithStandard(this Task task, object state, Action, object?> continuation) - { - return task.ContinueWith(continuation, state, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 157ef2dde7e4c..42361fba08085 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Net.Quic; -using System.Net.Quic.Implementations; using System.Net.Security; using System.Runtime.Versioning; using System.Threading; @@ -608,6 +605,17 @@ protected internal override Task SendAsync(HttpRequestMessa } } + Uri? requestUri = request.RequestUri; + if (requestUri is null || !requestUri.IsAbsoluteUri) + { + return new InvalidOperationException(SR.net_http_client_invalid_requesturi); + } + + if (!HttpUtilities.IsSupportedScheme(requestUri.Scheme)) + { + return new NotSupportedException(SR.Format(SR.net_http_unsupported_requesturi_scheme, requestUri.Scheme)); + } + return null; } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index a5448b012d238..61727faa43307 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -61,11 +61,21 @@ public void BaseAddress_InvalidUri_Throws() { using (var client = new HttpClient(new CustomResponseHandler((r, c) => Task.FromResult(new HttpResponseMessage())))) { - AssertExtensions.Throws("value", () => client.BaseAddress = new Uri("ftp://onlyhttpsupported")); AssertExtensions.Throws("value", () => client.BaseAddress = new Uri("/onlyabsolutesupported", UriKind.Relative)); } } + [Fact] + public void BaseAddress_UnknownScheme_DoesNotThrow() + { + using (var client = new HttpClient(new CustomResponseHandler((r, c) => Task.FromResult(new HttpResponseMessage())))) + { + client.BaseAddress = new Uri("blob://foo.bar"); + client.BaseAddress = new Uri("extensions://foo.bar"); + client.BaseAddress = new Uri("foobar://foo.bar"); + } + } + [Fact] public void Timeout_Roundtrip_Equal() { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs index 18b7fa9fe47cc..5b936425da168 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs @@ -63,6 +63,17 @@ public void Ctor_NullStringUri_Accepted() Assert.Null(rm.Content); } + [Fact] + public void Ctor_EmptyStringUri_Accepted() + { + var rm = new HttpRequestMessage(HttpMethod.Put, string.Empty); + + Assert.Null(rm.RequestUri); + Assert.Equal(HttpMethod.Put, rm.Method); + Assert.Equal(_expectedRequestMessageVersion, rm.Version); + Assert.Null(rm.Content); + } + [Fact] public void Ctor_RelativeUri_CorrectValues() { @@ -105,9 +116,9 @@ public void Ctor_NullMethod_ThrowsArgumentNullException() } [Fact] - public void Ctor_NonHttpUri_ThrowsArgumentException() + public void Ctor_NonHttpUri_DoesNotThrow() { - AssertExtensions.Throws("requestUri", () => new HttpRequestMessage(HttpMethod.Put, "ftp://example.com")); + new HttpRequestMessage(HttpMethod.Put, "ftp://example.com"); } [Fact] @@ -159,10 +170,10 @@ public void Properties_SetPropertiesAndGetTheirValue_MatchingValues() } [Fact] - public void RequestUri_SetNonHttpUri_ThrowsArgumentException() + public void RequestUri_SetNonHttpUri_DoesNotThrow() { var rm = new HttpRequestMessage(); - AssertExtensions.Throws("value", () => { rm.RequestUri = new Uri("ftp://example.com"); }); + rm.RequestUri = new Uri("ftp://example.com"); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 10e81d5f67919..d8b7ec4e712cd 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3205,4 +3205,100 @@ public SocketsHttpHandler_HttpClientHandler_Finalization_Http3_Mock(ITestOutputH protected override Version UseVersion => HttpVersion.Version30; protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.Mock; } + + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public abstract class SocketsHttpHandler_RequestValidationTest + { + protected abstract bool TestAsync { get; } + + [Fact] + public void Send_NullRequest_ThrowsArgumentNullException() + { + Assert.Throws("request", () => + { + var invoker = new HttpMessageInvoker(new SocketsHttpHandler()); + if (TestAsync) + { + invoker.SendAsync(null, CancellationToken.None); + } + else + { + invoker.Send(null, CancellationToken.None); + } + }); + } + + [Fact] + public void Send_NullRequestUri_ThrowsInvalidOperationException() + { + Throws(new HttpRequestMessage()); + } + + [Fact] + public void Send_RelativeRequestUri_ThrowsInvalidOperationException() + { + Throws(new HttpRequestMessage(HttpMethod.Get, new Uri("/relative", UriKind.Relative))); + } + + [Fact] + public void Send_UnsupportedRequestUriScheme_ThrowsNotSupportedException() + { + Throws(new HttpRequestMessage(HttpMethod.Get, "foo://foo.bar")); + } + + [Fact] + public void Send_MajorVersionZero_ThrowsNotSupportedException() + { + Throws(new HttpRequestMessage { Version = new Version(0, 42) }); + } + + [Fact] + public void Send_TransferEncodingChunkedWithNoContent_ThrowsHttpRequestException() + { + var request = new HttpRequestMessage(); + request.Headers.TransferEncodingChunked = true; + + HttpRequestException exception = Throws(request); + Assert.IsType(exception.InnerException); + } + + [Fact] + public void Send_Http10WithTransferEncodingChunked_ThrowsNotSupportedException() + { + var request = new HttpRequestMessage + { + Content = new StringContent("foo"), + Version = new Version(1, 0) + }; + request.Headers.TransferEncodingChunked = true; + + Throws(request); + } + + private TException Throws(HttpRequestMessage request) + where TException : Exception + { + var invoker = new HttpMessageInvoker(new SocketsHttpHandler()); + if (TestAsync) + { + Task task = invoker.SendAsync(request, CancellationToken.None); + Assert.Equal(TaskStatus.Faulted, task.Status); + return Assert.IsType(task.Exception.InnerException); + } + else + { + return Assert.Throws(() => invoker.Send(request, CancellationToken.None)); + } + } + } + + public sealed class SocketsHttpHandler_RequestValidationTest_Async : SocketsHttpHandler_RequestValidationTest + { + protected override bool TestAsync => true; + } + + public sealed class SocketsHttpHandler_RequestValidationTest_Sync : SocketsHttpHandler_RequestValidationTest + { + protected override bool TestAsync => false; + } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 3b950f2a36c9f..d6a2cc28d1113 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -218,8 +218,6 @@ Link="ProductionCode\System\Net\Http\HttpResponseMessage.cs" /> - + - diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs index 42e34d74156dd..39bbf05fef4bd 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs @@ -46,6 +46,7 @@ public void Ctor_RelativeStringUri_CorrectValues() [Theory] [InlineData("http://host/absolute/")] [InlineData("blob:http://host/absolute/")] + [InlineData("foo://host/absolute")] public void Ctor_AbsoluteStringUri_CorrectValues(string uri) { var rm = new HttpRequestMessage(HttpMethod.Post, uri); @@ -82,6 +83,7 @@ public void Ctor_RelativeUri_CorrectValues() [Theory] [InlineData("http://host/absolute/")] [InlineData("blob:http://host/absolute/")] + [InlineData("foo://host/absolute")] public void Ctor_AbsoluteUri_CorrectValues(string uriData) { var uri = new Uri(uriData); @@ -112,12 +114,6 @@ public void Ctor_NullMethod_ThrowsArgumentNullException(string uriData) Assert.Throws(() => new HttpRequestMessage(null, uriData)); } - [Fact] - public void Ctor_NonHttpUri_ThrowsArgumentException() - { - AssertExtensions.Throws("requestUri", () => new HttpRequestMessage(HttpMethod.Put, "ftp://example.com")); - } - [Theory] [InlineData("http://example.com")] [InlineData("blob:http://example.com")] @@ -304,14 +300,6 @@ public void Properties_SetOptionsAndGetTheirValue_NotSet_EnableStreamingResponse Assert.False(streamingEnabledValue); } - - [Fact] - public void RequestUri_SetNonHttpUri_ThrowsArgumentException() - { - var rm = new HttpRequestMessage(); - AssertExtensions.Throws("value", () => { rm.RequestUri = new Uri("ftp://example.com"); }); - } - [Fact] public void Version_SetToNull_ThrowsArgumentNullException() { From be830550d93d9264cce6406222258e9928617b9b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 9 Jul 2021 17:35:25 -0700 Subject: [PATCH 49/72] disable failing quic test (#55440) --- .../System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index c1f2e49beba8c..b8097a43a6c6d 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -117,6 +117,7 @@ public async Task ConnectWithCertificateChain() [Fact] [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue("https://github.com/microsoft/msquic/pull/1728")] public async Task ConnectWithClientCertificate() { bool clientCertificateOK = false; From f22685fbf3ffbbf149ca28ab7ab0d6a3c77b7665 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 9 Jul 2021 20:10:10 -0500 Subject: [PATCH 50/72] Update wasm workload name (#55413) * Update wasm workload name * Remove redirect-to it is not implmented yet --- .../WorkloadManifest.json.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index b4ddda95fe037..05d94220cb8b6 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -4,8 +4,8 @@ "Microsoft.NET.Workload.Emscripten": "${EmscriptenVersion}" }, "workloads": { - "microsoft-net-sdk-blazorwebassembly-aot": { - "description": "Browser Runtime native performance tools", + "wasm-tools": { + "description": ".NET WebAssembly build tools", "packs": [ "Microsoft.NET.Runtime.WebAssembly.Sdk", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", From ecea9a35d7aca60eeec5f728ddf284275f1c685c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 9 Jul 2021 21:03:04 -0500 Subject: [PATCH 51/72] Re-enable Windows_ZipWithInvalidFileNames tests (#55434) Since we have shipped for 3 years with the current behavior, re-enabling the test so it ensures the current behavior going forward. Extracting .zip files with invalid file names throw an IOException with a message "The filename, directory name, or volume label syntax is incorrect.". Contributes to #25099 --- .../tests/ZipFile.Extract.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs index 9bbfd271d2e47..e859959c07b05 100644 --- a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs +++ b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs @@ -94,15 +94,25 @@ public void Unix_ZipWithOSSpecificFileNames(string zipName, string fileName) /// when an attempt is made to extract them. /// [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/25099")] - [InlineData("WindowsInvalid_FromUnix", null)] - [InlineData("WindowsInvalid_FromWindows", null)] - [InlineData("NullCharFileName_FromWindows", "path")] - [InlineData("NullCharFileName_FromUnix", "path")] + [InlineData("NullCharFileName_FromWindows")] + [InlineData("NullCharFileName_FromUnix")] + [PlatformSpecific(TestPlatforms.Windows)] // Checks Windows-specific invalid file path + public void Windows_ZipWithInvalidFileNames_ThrowsArgumentException(string zipName) + { + AssertExtensions.Throws("path", null, () => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); + } + + /// + /// This test ensures that a zipfile with path names that are invalid to this OS will throw errors + /// when an attempt is made to extract them. + /// + [Theory] + [InlineData("WindowsInvalid_FromUnix")] + [InlineData("WindowsInvalid_FromWindows")] [PlatformSpecific(TestPlatforms.Windows)] // Checks Windows-specific invalid file path - public void Windows_ZipWithInvalidFileNames_ThrowsArgumentException(string zipName, string paramName) + public void Windows_ZipWithInvalidFileNames_ThrowsIOException(string zipName) { - AssertExtensions.Throws(paramName, null, () => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); + AssertExtensions.Throws(() => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); } [Theory] From 69d67390e5dd77cab116982085b294159d9ddcf8 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Fri, 9 Jul 2021 19:32:23 -0700 Subject: [PATCH 52/72] Fix the daily build links in docs (#55443) * Fix the daily build links in docs * Update the NuGet feed referenced --- docs/workflow/testing/using-your-build.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/workflow/testing/using-your-build.md b/docs/workflow/testing/using-your-build.md index 682a5dcb5234e..a6024d4269bd7 100644 --- a/docs/workflow/testing/using-your-build.md +++ b/docs/workflow/testing/using-your-build.md @@ -16,9 +16,9 @@ assume use of a dogfood build of the .NET SDK. ## Acquire the latest nightly .NET SDK -- [Win 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-win-x64.zip) -- [macOS 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-osx-x64.tar.gz) -- [Others](https://github.com/dotnet/cli/blob/master/README.md#installers-and-binaries) +- [Win 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip) +- [macOS 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.tar.gz) +- [Others](https://github.com/dotnet/installer#installers-and-binaries) To setup the SDK download the zip and extract it somewhere and add the root folder to your [path](../requirements/windows-requirements.md#adding-to-the-default-path-variable) or always fully qualify the path to dotnet in the root of this folder for all the instructions in this document. @@ -73,8 +73,7 @@ dotnet publish - - + ``` From 413f0de990022db48001b0bd6a36a151cc9daa51 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Sat, 10 Jul 2021 06:57:01 +0200 Subject: [PATCH 53/72] ILLink annotation related to ISerializable and IBindingListView in System.Data.Common (#55394) --- .../ref/System.ComponentModel.TypeConverter.cs | 2 +- .../System/ComponentModel/IBindingListView.cs | 2 +- .../ref/System.Data.Common.cs | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------------ .../src/System/Data/DataSet.cs | 2 ++ .../src/System/Data/DataTable.cs | 2 ++ .../src/System/Data/DataView.cs | 1 + 7 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index 6a3d62e34a7b0..b916f1c78782e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -538,7 +538,7 @@ public partial interface IBindingList : System.Collections.ICollection, System.C } public partial interface IBindingListView : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.ComponentModel.IBindingList { - string? Filter { get; set; } + string? Filter { get; [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members of types used in the filter expression might be trimmed.")] set; } System.ComponentModel.ListSortDescriptionCollection SortDescriptions { get; } bool SupportsAdvancedSorting { get; } bool SupportsFiltering { get; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs index 73b5035800fa1..f9fef2735bea9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs @@ -9,7 +9,7 @@ public interface IBindingListView : IBindingList { void ApplySort(ListSortDescriptionCollection sorts); - string? Filter { get; set; } + string? Filter { get; [RequiresUnreferencedCode("Members of types used in the filter expression might be trimmed.")] set; } ListSortDescriptionCollection SortDescriptions { get; } diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index ca955720c0df1..38a37217d2fa6 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -1003,7 +1003,7 @@ public DataView(System.Data.DataTable table, string? RowFilter, string? Sort, Sy bool System.ComponentModel.IBindingList.SupportsChangeNotification { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSearching { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSorting { get { throw null; } } - string? System.ComponentModel.IBindingListView.Filter { get { throw null; } set { } } + string? System.ComponentModel.IBindingListView.Filter { get { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members of types used in the filter expression might be trimmed.")] set { } } System.ComponentModel.ListSortDescriptionCollection System.ComponentModel.IBindingListView.SortDescriptions { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsAdvancedSorting { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsFiltering { get { throw null; } } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 9825521322ca8..393abfc15c492 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,12 +1,6 @@  - - ILLink - IL2026 - member - M:System.Data.DataSet.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) - ILLink IL2026 @@ -25,12 +19,6 @@ member M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataTable.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) - ILLink IL2026 @@ -49,11 +37,5 @@ member M:System.Data.DataTable.WriteXmlCore(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataView.System.ComponentModel.IBindingListView.set_Filter(System.String) - diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index 39410be06455b..33c265766cee4 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -268,6 +268,8 @@ protected DataSet(SerializationInfo info, StreamingContext context, bool Constru DeserializeDataSet(info, context, remotingFormat, schemaSerializationMode); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Binary serialization is unsafe in general and is planned to be obsoleted. We do not want to mark interface or ctors of this class as unsafe as that would show many unnecessary warnings elsewhere.")] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { SerializationFormat remotingFormat = RemotingFormat; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index be3c05ef7c350..bd8257122e233 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -211,6 +211,8 @@ protected DataTable(SerializationInfo info, StreamingContext context) : this() DeserializeDataTable(info, context, isSingleTable, remotingFormat); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Binary serialization is unsafe in general and is planned to be obsoleted. We do not want to mark interface or ctors of this class as unsafe as that would show many unnecessary warnings elsewhere.")] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { SerializationFormat remotingFormat = RemotingFormat; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataView.cs b/src/libraries/System.Data.Common/src/System/Data/DataView.cs index c72031a853369..6f9793333c506 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataView.cs @@ -1136,6 +1136,7 @@ void IBindingListView.RemoveFilter() string? IBindingListView.Filter { get { return RowFilter; } + [RequiresUnreferencedCode(Select.RequiresUnreferencedCodeMessage)] set { RowFilter = value; } } From 27b564ab96549df11611eda172992b1db5c6e96e Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Fri, 9 Jul 2021 23:22:06 -0700 Subject: [PATCH 54/72] Spill single-def variable at definition to avoid further spilling (#54345) * Print single-def * Rename lvEhWriteThruCandidate->lvSingleDefRegCandidate, introduce isSingleDef * Introduce singleDefSpillAfter If a single-def variable is decided to get spilled in its lifetime, then spill it at the firstRefPosition RefTypeDef so the value of the variable is always valid on the stack. Going forward, no more spills will be needed for such variable or no more resolutions (reg to stack) will be needed for such single-def variables. * jit format * some fixes * wip * Add check of isSingleDef in validateInterval() * Make isSingleDef during buildIntervals * minor fix in lclvars.cpp * some fixes after self CR * Updated some comments * Remove lvSpillAtSingleDef from some asserts * Use singleDefSpill information in getWeight() * Remove lvSpillAtSingleDef from some more checks * Mark lvSpillAtSingleDef whenever refPosition->singleDefSpill==true * Add TODO for SingleDefVarCandidate * Some notes on setting singleDefSpill * jit format * review feedback * review comments --- src/coreclr/jit/codegencommon.cpp | 14 +++--- src/coreclr/jit/codegenlinear.cpp | 27 +++++------ src/coreclr/jit/compiler.h | 27 +++++++++-- src/coreclr/jit/lclvars.cpp | 48 +++++++++++--------- src/coreclr/jit/lsra.cpp | 70 ++++++++++++++++++++++------- src/coreclr/jit/lsra.h | 10 +++++ src/coreclr/jit/lsrabuild.cpp | 38 +++++++++++----- src/coreclr/jit/morph.cpp | 18 +++++--- src/coreclr/jit/treelifeupdater.cpp | 6 +-- 9 files changed, 177 insertions(+), 81 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 99852d7f44612..aeecb0553b989 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -552,9 +552,9 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo else { // If this is going live, the register must not have a variable in it, except - // in the case of an exception variable, which may be already treated as live - // in the register. - assert(varDsc->lvLiveInOutOfHndlr || ((regSet.GetMaskVars() & regMask) == 0)); + // in the case of an exception or "spill at single-def" variable, which may be already treated + // as live in the register. + assert(varDsc->IsAlwaysAliveInMemory() || ((regSet.GetMaskVars() & regMask) == 0)); regSet.AddMaskVars(regMask); } } @@ -736,7 +736,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) bool isGCRef = (varDsc->TypeGet() == TYP_REF); bool isByRef = (varDsc->TypeGet() == TYP_BYREF); bool isInReg = varDsc->lvIsInReg(); - bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); if (isInReg) { @@ -777,8 +777,8 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) if (varDsc->lvIsInReg()) { // If this variable is going live in a register, it is no longer live on the stack, - // unless it is an EH var, which always remains live on the stack. - if (!varDsc->lvLiveInOutOfHndlr) + // unless it is an EH/"spill at single-def" var, which always remains live on the stack. + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex)) @@ -11422,7 +11422,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) { varReg = REG_STK; } - if ((varReg == REG_STK) || fieldVarDsc->lvLiveInOutOfHndlr) + if ((varReg == REG_STK) || fieldVarDsc->IsAlwaysAliveInMemory()) { if (!lclNode->AsLclVar()->IsLastUse(i)) { diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index eb942332554f3..b822f233e66db 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -229,7 +229,7 @@ void CodeGen::genCodeForBBlist() { newRegByrefSet |= varDsc->lvRegMask(); } - if (!varDsc->lvLiveInOutOfHndlr) + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (verbose && VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) @@ -240,7 +240,7 @@ void CodeGen::genCodeForBBlist() VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex); } } - if ((!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr) && compiler->lvaIsGCTracked(varDsc)) + if ((!varDsc->lvIsInReg() || varDsc->IsAlwaysAliveInMemory()) && compiler->lvaIsGCTracked(varDsc)) { #ifdef DEBUG if (verbose && !VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) @@ -873,9 +873,9 @@ void CodeGen::genSpillVar(GenTree* tree) var_types lclType = varDsc->GetActualRegisterType(); emitAttr size = emitTypeSize(lclType); - // If this is a write-thru variable, we don't actually spill at a use, but we will kill the var in the reg - // (below). - if (!varDsc->lvLiveInOutOfHndlr) + // If this is a write-thru or a single-def variable, we don't actually spill at a use, + // but we will kill the var in the reg (below). + if (!varDsc->IsAlwaysAliveInMemory()) { instruction storeIns = ins_Store(lclType, compiler->isSIMDTypeLocalAligned(varNum)); assert(varDsc->GetRegNum() == tree->GetRegNum()); @@ -883,7 +883,7 @@ void CodeGen::genSpillVar(GenTree* tree) } // We should only have both GTF_SPILL (i.e. the flag causing this method to be called) and - // GTF_SPILLED on a write-thru def, for which we should not be calling this method. + // GTF_SPILLED on a write-thru/single-def def, for which we should not be calling this method. assert((tree->gtFlags & GTF_SPILLED) == 0); // Remove the live var from the register. @@ -918,8 +918,9 @@ void CodeGen::genSpillVar(GenTree* tree) } else { - // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar. - assert(varDsc->lvLiveInOutOfHndlr && ((tree->gtFlags & GTF_VAR_DEF) != 0)); + // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar + // or a single-def var that is to be spilled at its definition. + assert((varDsc->IsAlwaysAliveInMemory()) && ((tree->gtFlags & GTF_VAR_DEF) != 0)); } #ifdef USING_VARIABLE_LIVE_RANGE @@ -1055,7 +1056,7 @@ void CodeGen::genUnspillLocal( } #endif // USING_VARIABLE_LIVE_RANGE - if (!varDsc->lvLiveInOutOfHndlr) + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) @@ -2044,12 +2045,12 @@ void CodeGen::genSpillLocal(unsigned varNum, var_types type, GenTreeLclVar* lclN // We have a register candidate local that is marked with GTF_SPILL. // This flag generally means that we need to spill this local. - // The exception is the case of a use of an EH var use that is being "spilled" + // The exception is the case of a use of an EH/spill-at-single-def var use that is being "spilled" // to the stack, indicated by GTF_SPILL (note that all EH lclVar defs are always - // spilled, i.e. write-thru). - // An EH var use is always valid on the stack (so we don't need to actually spill it), + // spilled, i.e. write-thru. Likewise, single-def vars that are spilled at its definitions). + // An EH or single-def var use is always valid on the stack (so we don't need to actually spill it), // but the GTF_SPILL flag records the fact that the register value is going dead. - if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || !varDsc->lvLiveInOutOfHndlr) + if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || (!varDsc->IsAlwaysAliveInMemory())) { // Store local variable to its home location. // Ensure that lclVar stores are typed correctly. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f816e6917a604..1383b57a7dd3a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -446,10 +446,19 @@ class LclVarDsc // before lvaMarkLocalVars: identifies ref type locals that can get type updates // after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies - unsigned char lvEhWriteThruCandidate : 1; // variable has a single def and hence is a register candidate if - // if it is an EH variable + unsigned char lvSingleDefRegCandidate : 1; // variable has a single def and hence is a register candidate + // Currently, this is only used to decide if an EH variable can be + // a register candiate or not. - unsigned char lvDisqualifyForEhWriteThru : 1; // tracks variable that are disqualified from register candidancy + unsigned char lvDisqualifySingleDefRegCandidate : 1; // tracks variable that are disqualified from register + // candidancy + + unsigned char lvSpillAtSingleDef : 1; // variable has a single def (as determined by LSRA interval scan) + // and is spilled making it candidate to spill right after the + // first (and only) definition. + // Note: We cannot reuse lvSingleDefRegCandidate because it is set + // in earlier phase and the information might not be appropriate + // in LSRA. #if ASSERTION_PROP unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization @@ -547,7 +556,7 @@ class LclVarDsc unsigned char lvFldOrdinal; #ifdef DEBUG - unsigned char lvDisqualifyEHVarReason = 'H'; + unsigned char lvSingleDefDisqualifyReason = 'H'; #endif #if FEATURE_MULTIREG_ARGS @@ -1030,6 +1039,16 @@ class LclVarDsc return IsEnregisterableType(); } + //----------------------------------------------------------------------------- + // IsAlwaysAliveInMemory: Determines if this variable's value is always + // up-to-date on stack. This is possible if this is an EH-var or + // we decided to spill after single-def. + // + bool IsAlwaysAliveInMemory() const + { + return lvLiveInOutOfHndlr || lvSpillAtSingleDef; + } + bool CanBeReplacedWithItsField(Compiler* comp) const; #ifdef DEBUG diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index b2ca56b406ba4..33c5abaa30df7 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2559,7 +2559,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum) noway_assert(lvaTable[i].lvIsStructField); lvaTable[i].lvLiveInOutOfHndlr = 1; // For now, only enregister an EH Var if it is a single def and whose refCnt > 1. - if (!lvaEnregEHVars || !lvaTable[i].lvEhWriteThruCandidate || lvaTable[i].lvRefCnt() <= 1) + if (!lvaEnregEHVars || !lvaTable[i].lvSingleDefRegCandidate || lvaTable[i].lvRefCnt() <= 1) { lvaSetVarDoNotEnregister(i DEBUGARG(DNER_LiveInOutOfHandler)); } @@ -2567,7 +2567,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum) } // For now, only enregister an EH Var if it is a single def and whose refCnt > 1. - if (!lvaEnregEHVars || !varDsc->lvEhWriteThruCandidate || varDsc->lvRefCnt() <= 1) + if (!lvaEnregEHVars || !varDsc->lvSingleDefRegCandidate || varDsc->lvRefCnt() <= 1) { lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LiveInOutOfHandler)); } @@ -4110,33 +4110,35 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, } } - if (!varDsc->lvDisqualifyForEhWriteThru) // If this EH var already disqualified, we can skip this + if (!varDsc->lvDisqualifySingleDefRegCandidate) // If this var is already disqualified, we can skip this { if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable { - bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0; - bool bbIsReturn = block->bbJumpKind == BBJ_RETURN; + bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0; + bool bbIsReturn = block->bbJumpKind == BBJ_RETURN; + // TODO: Zero-inits in LSRA are created with below condition. Try to use similar condition here as well. + // if (compiler->info.compInitMem || varTypeIsGC(varDsc->TypeGet())) bool needsExplicitZeroInit = fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn); - if (varDsc->lvEhWriteThruCandidate || needsExplicitZeroInit) + if (varDsc->lvSingleDefRegCandidate || needsExplicitZeroInit) { #ifdef DEBUG if (needsExplicitZeroInit) { - varDsc->lvDisqualifyEHVarReason = 'Z'; - JITDUMP("EH Var V%02u needs explicit zero init. Disqualified as a register candidate.\n", + varDsc->lvSingleDefDisqualifyReason = 'Z'; + JITDUMP("V%02u needs explicit zero init. Disqualified as a single-def register candidate.\n", lclNum); } else { - varDsc->lvDisqualifyEHVarReason = 'M'; - JITDUMP("EH Var V%02u has multiple definitions. Disqualified as a register candidate.\n", + varDsc->lvSingleDefDisqualifyReason = 'M'; + JITDUMP("V%02u has multiple definitions. Disqualified as a single-def register candidate.\n", lclNum); } #endif // DEBUG - varDsc->lvEhWriteThruCandidate = false; - varDsc->lvDisqualifyForEhWriteThru = true; + varDsc->lvSingleDefRegCandidate = false; + varDsc->lvDisqualifySingleDefRegCandidate = true; } else { @@ -4146,7 +4148,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, if (!varTypeNeedsPartialCalleeSave(varDsc->lvType)) #endif { - varDsc->lvEhWriteThruCandidate = true; + varDsc->lvSingleDefRegCandidate = true; JITDUMP("Marking EH Var V%02u as a register candidate.\n", lclNum); } } @@ -4521,8 +4523,8 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers) // that was set by past phases. if (!isRecompute) { - varDsc->lvSingleDef = varDsc->lvIsParam; - varDsc->lvEhWriteThruCandidate = varDsc->lvIsParam; + varDsc->lvSingleDef = varDsc->lvIsParam; + varDsc->lvSingleDefRegCandidate = varDsc->lvIsParam; } } @@ -7405,11 +7407,6 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r printf(" HFA(%s) ", varTypeName(varDsc->GetHfaType())); } - if (varDsc->lvLiveInOutOfHndlr) - { - printf(" EH"); - } - if (varDsc->lvDoNotEnregister) { printf(" do-not-enreg["); @@ -7427,7 +7424,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r } if (lvaEnregEHVars && varDsc->lvLiveInOutOfHndlr) { - printf("%c", varDsc->lvDisqualifyEHVarReason); + printf("%c", varDsc->lvSingleDefDisqualifyReason); } if (varDsc->lvLclFieldExpr) { @@ -7500,6 +7497,15 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r { printf(" EH-live"); } + if (varDsc->lvSpillAtSingleDef) + { + printf(" spill-single-def"); + } + else if (varDsc->lvSingleDefRegCandidate) + { + printf(" single-def"); + } + #ifndef TARGET_64BIT if (varDsc->lvStructDoubleAlign) printf(" double-align"); diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index f810ca3d3aa35..4bbb877fda8c3 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -196,9 +196,9 @@ BasicBlock::weight_t LinearScan::getWeight(RefPosition* refPos) if (refPos->getInterval()->isSpilled) { // Decrease the weight if the interval has already been spilled. - if (varDsc->lvLiveInOutOfHndlr) + if (varDsc->lvLiveInOutOfHndlr || refPos->getInterval()->firstRefPosition->singleDefSpill) { - // An EH var is always spilled at defs, and we'll decrease the weight by half, + // An EH-var/single-def is always spilled at defs, and we'll decrease the weight by half, // since only the reload is needed. weight = weight / 2; } @@ -1794,7 +1794,7 @@ void LinearScan::identifyCandidates() if (varDsc->lvLiveInOutOfHndlr) { - newInt->isWriteThru = varDsc->lvEhWriteThruCandidate; + newInt->isWriteThru = varDsc->lvSingleDefRegCandidate; setIntervalAsSpilled(newInt); } @@ -3273,6 +3273,24 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition fromRefPosition->spillAfter = true; } } + + // Only handle the singledef intervals whose firstRefPosition is RefTypeDef and is not yet marked as spillAfter. + // The singledef intervals whose firstRefPositions are already marked as spillAfter, no need to mark them as + // singleDefSpill because they will always get spilled at firstRefPosition. + // This helps in spilling the singleDef at definition + // + // Note: Only mark "singleDefSpill" for those intervals who ever get spilled. The intervals that are never spilled + // will not be marked as "singleDefSpill" and hence won't get spilled at the first definition. + if (interval->isSingleDef && RefTypeIsDef(interval->firstRefPosition->refType) && + !interval->firstRefPosition->spillAfter) + { + // TODO-CQ: Check if it is beneficial to spill at def, meaning, if it is a hot block don't worry about + // doing the spill. Another option is to track number of refpositions and a interval has more than X + // refpositions + // then perform this optimization. + interval->firstRefPosition->singleDefSpill = true; + } + assert(toRefPosition != nullptr); #ifdef DEBUG @@ -3955,16 +3973,16 @@ void LinearScan::unassignIntervalBlockStart(RegRecord* regRecord, VarToRegMap in // // Arguments: // currentBlock - the BasicBlock we are about to allocate registers for -// allocationPass - true if we are currently allocating registers (versus writing them back) // // Return Value: // None // // Notes: -// During the allocation pass, we use the outVarToRegMap of the selected predecessor to -// determine the lclVar locations for the inVarToRegMap. -// During the resolution (write-back) pass, we only modify the inVarToRegMap in cases where -// a lclVar was spilled after the block had been completed. +// During the allocation pass (allocationPassComplete = false), we use the outVarToRegMap +// of the selected predecessor to determine the lclVar locations for the inVarToRegMap. +// During the resolution (write-back when allocationPassComplete = true) pass, we only +// modify the inVarToRegMap in cases where a lclVar was spilled after the block had been +// completed. void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) { // If we have no register candidates we should only call this method during allocation. @@ -5915,7 +5933,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref assert(currentRefPosition->refType == RefTypeExpUse); } } - else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) && + else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) && (treeNode != nullptr) && (!treeNode->IsMultiReg() || treeNode->gtGetOp1()->IsMultiRegNode())) { // In the case of a pure def, don't bother spilling - just assign it to the @@ -5926,10 +5944,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref assert(interval->isSpilled); varDsc->SetRegNum(REG_STK); interval->physReg = REG_NA; - if (treeNode != nullptr) - { - writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA); - } + writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA); } else // Not reload and Not pure-def that's spillAfter { @@ -6018,6 +6033,27 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref } } } + + if (currentRefPosition->singleDefSpill && (treeNode != nullptr)) + { + // This is the first (and only) def of a single-def var (only defs are marked 'singleDefSpill'). + // Mark it as GTF_SPILL, so it is spilled immediately to the stack at definition and + // GTF_SPILLED, so the variable stays live in the register. + // + // TODO: This approach would still create the resolution moves but during codegen, will check for + // `lvSpillAtSingleDef` to decide whether to generate spill or not. In future, see if there is some + // better way to avoid resolution moves, perhaps by updating the varDsc->SetRegNum(REG_STK) in this + // method? + treeNode->gtFlags |= GTF_SPILL; + treeNode->gtFlags |= GTF_SPILLED; + + if (treeNode->IsMultiReg()) + { + treeNode->SetRegSpillFlagByIdx(GTF_SPILLED, currentRefPosition->getMultiRegIdx()); + } + + varDsc->lvSpillAtSingleDef = true; + } } // Update the physRegRecord for the register, so that we know what vars are in @@ -8936,6 +8972,10 @@ void RefPosition::dump(LinearScan* linearScan) { printf(" spillAfter"); } + if (this->singleDefSpill) + { + printf(" singleDefSpill"); + } if (this->writeThru) { printf(" writeThru"); @@ -9678,12 +9718,12 @@ void LinearScan::dumpLsraAllocationEvent( case LSRA_EVENT_DONE_KILL_GC_REFS: dumpRefPositionShort(activeRefPosition, currentBlock); - printf("Done "); + printf("Done "); break; case LSRA_EVENT_NO_GC_KILLS: dumpRefPositionShort(activeRefPosition, currentBlock); - printf("None "); + printf("None "); break; // Block boundaries diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index 10ff1f471b4a7..b0c1735871695 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1928,6 +1928,7 @@ class Interval : public Referenceable , isPartiallySpilled(false) #endif , isWriteThru(false) + , isSingleDef(false) #ifdef DEBUG , intervalIndex(0) #endif @@ -2023,6 +2024,9 @@ class Interval : public Referenceable // True if this interval is associated with a lclVar that is written to memory at each definition. bool isWriteThru : 1; + // True if this interval has a single definition. + bool isSingleDef : 1; + #ifdef DEBUG unsigned int intervalIndex; #endif // DEBUG @@ -2222,6 +2226,10 @@ class RefPosition // Spill and Copy info // reload indicates that the value was spilled, and must be reloaded here. // spillAfter indicates that the value is spilled here, so a spill must be added. + // singleDefSpill indicates that it is associated with a single-def var and if it + // is decided to get spilled, it will be spilled at firstRefPosition def. That + // way, the the value of stack will always be up-to-date and no more spills or + // resolutions (from reg to stack) will be needed for such single-def var. // copyReg indicates that the value needs to be copied to a specific register, // but that it will also retain its current assigned register. // moveReg indicates that the value needs to be moved to a different register, @@ -2240,6 +2248,7 @@ class RefPosition unsigned char reload : 1; unsigned char spillAfter : 1; + unsigned char singleDefSpill : 1; unsigned char writeThru : 1; // true if this var is defined in a register and also spilled. spillAfter must NOT be // set. @@ -2287,6 +2296,7 @@ class RefPosition , lastUse(false) , reload(false) , spillAfter(false) + , singleDefSpill(false) , writeThru(false) , copyReg(false) , moveReg(false) diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index cb04ddddf51c0..f5f4d781d759e 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -620,6 +620,12 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval, associateRefPosWithInterval(newRP); + if (RefTypeIsDef(newRP->refType)) + { + assert(theInterval != nullptr); + theInterval->isSingleDef = theInterval->firstRefPosition == newRP; + } + DBEXEC(VERBOSE, newRP->dump(this)); return newRP; } @@ -2602,20 +2608,20 @@ void LinearScan::buildIntervals() { lsraDumpIntervals("BEFORE VALIDATING INTERVALS"); dumpRefPositions("BEFORE VALIDATING INTERVALS"); - validateIntervals(); } + validateIntervals(); + #endif // DEBUG } #ifdef DEBUG //------------------------------------------------------------------------ -// validateIntervals: A DEBUG-only method that checks that the lclVar RefPositions -// do not reflect uses of undefined values -// -// Notes: If an undefined use is encountered, it merely prints a message. +// validateIntervals: A DEBUG-only method that checks that: +// - the lclVar RefPositions do not reflect uses of undefined values +// - A singleDef interval should have just first RefPosition as RefTypeDef. // -// TODO-Cleanup: This should probably assert, or at least print the message only -// when doing a JITDUMP. +// TODO-Cleanup: If an undefined use is encountered, it merely prints a message +// but probably assert. // void LinearScan::validateIntervals() { @@ -2630,19 +2636,29 @@ void LinearScan::validateIntervals() Interval* interval = getIntervalForLocalVar(i); bool defined = false; - printf("-----------------\n"); + JITDUMP("-----------------\n"); for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition) { - ref->dump(this); + if (VERBOSE) + { + ref->dump(this); + } RefType refType = ref->refType; if (!defined && RefTypeIsUse(refType)) { if (compiler->info.compMethodName != nullptr) { - printf("%s: ", compiler->info.compMethodName); + JITDUMP("%s: ", compiler->info.compMethodName); } - printf("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation); + JITDUMP("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation); + } + + // For single-def intervals, the only the first refposition should be a RefTypeDef + if (interval->isSingleDef && RefTypeIsDef(refType)) + { + assert(ref == interval->firstRefPosition); } + // Note that there can be multiple last uses if they are on disjoint paths, // so we can't really check the lastUse flag if (ref->lastUse) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index daca2c487e2f0..526a996e1b40c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -17441,14 +17441,18 @@ void Compiler::fgRetypeImplicitByRefArgs() #endif // DEBUG // Propagate address-taken-ness and do-not-enregister-ness. - newVarDsc->lvAddrExposed = varDsc->lvAddrExposed; - newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister; + newVarDsc->lvAddrExposed = varDsc->lvAddrExposed; + newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister; + newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr; + newVarDsc->lvSingleDef = varDsc->lvSingleDef; + newVarDsc->lvSingleDefRegCandidate = varDsc->lvSingleDefRegCandidate; + newVarDsc->lvSpillAtSingleDef = varDsc->lvSpillAtSingleDef; #ifdef DEBUG - newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr; - newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr; - newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr; - newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr; - newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall; + newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr; + newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr; + newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr; + newVarDsc->lvSingleDefDisqualifyReason = varDsc->lvSingleDefDisqualifyReason; + newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall; #endif // DEBUG // If the promotion is dependent, the promoted temp would just be committed diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp index 20a9745362b57..15c32596cc422 100644 --- a/src/coreclr/jit/treelifeupdater.cpp +++ b/src/coreclr/jit/treelifeupdater.cpp @@ -60,7 +60,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns { regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex); bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; + isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); if (isInReg) { if (isBorn) @@ -259,7 +259,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) compiler->codeGen->genUpdateVarReg(varDsc, tree); } bool isInReg = varDsc->lvIsInReg() && tree->GetRegNum() != REG_NA; - bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); if (isInReg) { compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree)); @@ -283,7 +283,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) unsigned fldVarIndex = fldVarDsc->lvVarIndex; regNumber reg = lclVarTree->AsLclVar()->GetRegNumByIdx(i); bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - bool isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); bool isFieldDying = lclVarTree->AsLclVar()->IsLastUse(i); if ((isBorn && !isFieldDying) || (!isBorn && isFieldDying)) { From 739218439bb6d3b224e240c012f37c516ccd29c9 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sat, 10 Jul 2021 05:02:38 -0400 Subject: [PATCH 55/72] [mono] Remove one more reference to MONO_CORLIB_VERSION. (#55345) --- src/mono/cmake/config.h.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index d684efa2b2e0e..8e0c462a53ef3 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -71,9 +71,6 @@ /* Define to the home page for this package. */ #cmakedefine PACKAGE_URL 1 -/* Version of the corlib-runtime interface */ -#cmakedefine MONO_CORLIB_VERSION "@MONO_CORLIB_VERSION@" - /* Disables the IO portability layer */ #cmakedefine DISABLE_PORTABILITY 1 From bb394065f0136ce2bd3afb4349e1dbf46dc9012c Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sat, 10 Jul 2021 06:24:00 -0400 Subject: [PATCH 56/72] [wasm] Fix property name for Emscripten manifest nuget (#55444) .. this allows the version to get updated in `eng/Versions.props` also, when updates flow in. - And update the version to match Version.Details.xml --- eng/Versions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 8debb7be68dd1..7be0ee173f2f3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -180,7 +180,7 @@ 11.1.0-alpha.1.21357.1 11.1.0-alpha.1.21357.1 - 6.0.0-preview.7.21355.1 - $(MicrosoftNETWorkloadEmscriptenManifest60100) + 6.0.0-preview.7.21358.1 + $(MicrosoftNETWorkloadEmscriptenManifest60100Version) From 65b472a0f31f2b878601b41a344754ec3e776d1d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 10 Jul 2021 12:38:33 +0200 Subject: [PATCH 57/72] Handle a missing case in zero-extend peephole (#55129) --- src/coreclr/jit/emitxarch.cpp | 7 ++ .../JitBlue/Runtime_55129/Runtime_55129.cs | 101 ++++++++++++++++++ .../Runtime_55129/Runtime_55129.csproj | 10 ++ 3 files changed, 118 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index a0a5e3283d4e9..217f5f15aafb2 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -296,6 +296,13 @@ bool emitter::AreUpper32BitsZero(regNumber reg) return false; } +#ifdef TARGET_AMD64 + if (id->idIns() == INS_movsxd) + { + return false; + } +#endif + // movzx always zeroes the upper 32 bits. if (id->idIns() == INS_movzx) { diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs new file mode 100644 index 0000000000000..2ee1747740866 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +public class Runtime_55129 +{ + public static int Main() + { + int result = 100; + if (!Runtime_55129_1.Run()) + result |= 1; + if (!Runtime_55129_2.Run()) + result |= 2; + return result; + } +} + +// These tests failed because of a missing zero extension because a peephole +// did not handle that 'movsxd' would sign extend. +public class Runtime_55129_1 +{ + static I s_i = new C(); + static short s_7; + static sbyte[][] s_10 = new sbyte[][]{new sbyte[]{-1}}; + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Run() + { + var vr59 = (uint)M6(s_10[0][0]); + return (long)vr59 == uint.MaxValue; + } + + static ulong M6(sbyte arg0) + { + return (ulong)arg0; + ref short var1 = ref s_7; + s_i.Foo(var1); + } +} + +interface I +{ + void Foo(T val); +} + +class C : I +{ + public void Foo(T val) { } +} + +struct S0 +{ + public long F5; + public S0(int f0, byte f1, ulong f2, byte f3, uint f4, long f5, int f6, int f7) : this() + { + } +} + +class C0 +{ + public long F0; +} + +class C1 +{ + public ulong F1; +} + +public class Runtime_55129_2 +{ + static int[] s_2 = new int[] { -1 }; + static C0 s_4 = new C0(); + static S0 s_5; + static C1[][] s_47 = new C1[][] { new C1[] { new C1() } }; + static bool s_result; + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Run() + { + s_5.F5 = s_2[0]; + C1 vr4 = s_47[0][0]; + var vr6 = vr4.F1; + M6(vr6); + return s_result; + } + + static void M6(ulong arg0) + { + arg0 >>= 0; + if (-1 < (uint)(0U | M7(ref s_4.F0, new S0[][,] { new S0[,] { { new S0(-10, 1, 0, 178, 1671790506U, -2L, 1, -2147483648) } }, new S0[,] { { new S0(1330389305, 255, 1297834355652867458UL, 0, 1777203966U, 4402572156859115751L, -1597826478, 1) } }, new S0[,] { { new S0(2147483646, 15, 18446744073709551614UL, 9, 1089668776U, 8629324174561266356L, 2124906017, -1883510008) } } }, 1, new sbyte[] { -37, -21, 0, 0, 0, 0 }, new S0[] { new S0(219671235, 22, 11763641210444381762UL, 0, 2568868236U, -7432636731544997849L, 1623417447, -479936755), new S0(-2147483647, 108, 0, 1, 4294967294U, 9223372036854775807L, 539462011, 1), new S0(1, 0, 15733997012901423027UL, 212, 4294967294U, 4663434921694141184L, -2147483647, 1196938120), new S0(1, 68, 0, 14, 653907833U, -6962955672558660864L, 1966270988, -378944819) }))) + { + s_result = true; + } + } + + static short M7(ref long arg0, S0[][,] arg1, ushort arg2, sbyte[] arg3, S0[] arg4) + { + long vr20 = s_5.F5; + return (short)vr20; + } +} + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj new file mode 100644 index 0000000000000..1100f420532dc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 5e657717a7026638ec03a807114de87cc484c677 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 19:59:49 +0300 Subject: [PATCH 58/72] [interp] Fix pinvokes with HandleRef (#55404) The m2n wrapper marshals HandleRef structs from a vtype to intptr. The MonoMethod for a pinvoke method stores the unmarshalled signature. When locating the args on the stack during the pinvoke call we need to use the marshalled signature instead (which is saved in the code stream for the calli opcode) --- src/mono/mono/mini/interp/interp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index dad5f095b0026..f499c9756cc9a 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1173,12 +1173,16 @@ compute_arg_offset (MonoMethodSignature *sig, int index, int prev_offset) } static guint32* -initialize_arg_offsets (InterpMethod *imethod) +initialize_arg_offsets (InterpMethod *imethod, MonoMethodSignature *csig) { if (imethod->arg_offsets) return imethod->arg_offsets; - MonoMethodSignature *sig = mono_method_signature_internal (imethod->method); + // For pinvokes, csig represents the real signature with marshalled args. If an explicit + // marshalled signature was not provided, we use the managed signature of the method. + MonoMethodSignature *sig = csig; + if (!sig) + sig = mono_method_signature_internal (imethod->method); int arg_count = sig->hasthis + sig->param_count; g_assert (arg_count); guint32 *arg_offsets = (guint32*) g_malloc ((sig->hasthis + sig->param_count) * sizeof (int)); @@ -1201,13 +1205,13 @@ initialize_arg_offsets (InterpMethod *imethod) } static guint32 -get_arg_offset_fast (InterpMethod *imethod, int index) +get_arg_offset_fast (InterpMethod *imethod, MonoMethodSignature *sig, int index) { guint32 *arg_offsets = imethod->arg_offsets; if (arg_offsets) return arg_offsets [index]; - arg_offsets = initialize_arg_offsets (imethod); + arg_offsets = initialize_arg_offsets (imethod, sig); g_assert (arg_offsets); return arg_offsets [index]; } @@ -1216,7 +1220,7 @@ static guint32 get_arg_offset (InterpMethod *imethod, MonoMethodSignature *sig, int index) { if (imethod) { - return get_arg_offset_fast (imethod, index); + return get_arg_offset_fast (imethod, sig, index); } else { g_assert (!sig->hasthis); return compute_arg_offset (sig, index, -1); @@ -2385,7 +2389,7 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame if (cinfo->ret_mt != -1) args [pindex ++] = ret_sp; for (int i = 0; i < rmethod->param_count; ++i) { - stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, stack_index + i)); + stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, NULL, stack_index + i)); if (cinfo->arginfo [i] == JIT_ARG_BYVAL) args [pindex ++] = sval->data.p; else @@ -7199,7 +7203,7 @@ interp_frame_get_arg (MonoInterpFrameHandle frame, int pos) g_assert (iframe->imethod); - return (char*)iframe->stack + get_arg_offset_fast (iframe->imethod, pos + iframe->imethod->hasthis); + return (char*)iframe->stack + get_arg_offset_fast (iframe->imethod, NULL, pos + iframe->imethod->hasthis); } static gpointer From 1ff0b50118985994306052494822585710992410 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 20:00:38 +0300 Subject: [PATCH 59/72] [interp] Fix open delegates used with virtual methods of valuetypes (#55354) * [interp] Fix open delegates used with virtual methods of valuetypes We were trying to resolve the virtual method on the this pointer (which is a managed pointer and not an object). If the delegate target method is declared on a valuetype, the method does not need resolving. Fixes https://github.com/dotnet/runtime/issues/49839 * Re-enable tests --- src/libraries/System.Runtime/tests/System/DelegateTests.cs | 2 -- src/mono/mono/mini/interp/interp.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System/DelegateTests.cs index 269b5f7b218af..9a3658457c10d 100644 --- a/src/libraries/System.Runtime/tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System/DelegateTests.cs @@ -1118,7 +1118,6 @@ public static void CreateDelegate9_Type_Null() Assert.NotNull(ex.Message); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] [Fact] public static void CreateDelegate10_Nullable_Method() { @@ -1130,7 +1129,6 @@ public static void CreateDelegate10_Nullable_Method() Assert.Equal(num.ToString(), s); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] [Fact] public static void CreateDelegate10_Nullable_ClosedDelegate() { diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index f499c9756cc9a..44d1c70b396bc 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3437,7 +3437,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs del_imethod = mono_interp_get_imethod (mono_marshal_get_native_wrapper (del_imethod->method, FALSE, FALSE), error); mono_error_assert_ok (error); del->interp_invoke_impl = del_imethod; - } else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target) { + } else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target && !m_class_is_valuetype (del_imethod->method->klass)) { // 'this' is passed dynamically, we need to recompute the target method // with each call del_imethod = get_virtual_method (del_imethod, LOCAL_VAR (call_args_offset + MINT_STACK_SLOT_SIZE, MonoObject*)->vtable); From 02ccdacb069ff0608b7ec672afe206721aaa50b7 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 10 Jul 2021 10:17:54 -0700 Subject: [PATCH 60/72] Improve loop cloning, with debugging improvements (#55299) When loop cloning was creating cloning conditions, it was creating unnecessary bounds checks in some multi-dimensional array index cases. When creating a set of cloning conditions, first a null check is done, then an array length check is done, etc. Thus, the array length expression itself won't fault because we've already done a null check. And a subsequent array index expression won't fault (or need a bounds check) because we've already checked the array length (i.e., we've done a manual bounds check). So, stop creating the unnecessary bounds checks, and mark the appropriate instructions as non-faulting by clearing the GTF_EXCEPT bit. Note that I did not turn on the code to clear GTF_EXCEPT for array length checks because it leads to negative downstream effects in CSE. Namely, there end up being array length expressions that are identical except for the exception bit. When CSE sees this, it gives up on creating a CSE, which leads to regressions in some cases where we don't CSE the array length expression. Also, for multi-dimension jagged arrays, when optimizing the fast path, we were not removing as many bounds checks as we could. In particular, we weren't removing outer bounds checks, only inner ones. Add code to handle all the bounds checks. There are some runtime improvements (measured via BenchmarkDotNet on the JIT microbenchmarks), but also some regressions, due, as far as I can tell, to the Intel jcc erratum performance impact. In particular, benchmark ludcmp shows up to a 9% regression due to a `jae` instruction in the hot loop now crossing a 32-byte boundary due to code changes earlier in the function affecting instruction alignment. The hot loop itself is exactly the same (module register allocation differences). As there is nothing that can be done (without mitigating the jcc erratum) -- it's "bad luck". In addition to those functional changes, there are a number of debugging-related improvements: 1. Loop cloning: (a) Improved dumping of cloning conditions and other things, (b) remove an unnecessary member to `LcOptInfo`, (c) convert the `LoopCloneContext` raw arrays to `jitstd::vector` for easier debugging, as clrjit.natvis can be taught to understand them. 2. CSE improvements: (a) Add `getCSEAvailBit` and `getCSEAvailCrossCallBit` functions to avoid multiple hard-codings of these expresions, (b) stop printing all the details of the CSE dataflow to JitDump; just print the result, (c) add `optPrintCSEDataFlowSet` function to print the CSE dataflow set in symbolic form, not just the raw bits, (d) added `FMT_CSE` string to use for formatting CSE candidates, (e) added `optOptimizeCSEs` to the phase structure for JitDump output, (f) remove unused `optCSECandidateTotal` (remnant of Valnum + lexical CSE) 3. Alignment: (a) Moved printing of alignment boundaries from `emitIssue1Instr` to `emitEndCodeGen`, to avoid the possibility of reading an instruction beyond the basic block. Also, improved the Intel jcc erratum criteria calculations, (b) Change `align` instructions of zero size to have a zero PerfScore throughput number (since they don't generate code), (c) Add `COMPlus_JitDasmWithAlignmentBoundaries` to force disasm output to display alignment boundaries. 4. Codegen / Emitter: (a) Added `emitLabelString` function for constructing a string to display for a bound emitter label. Created `emitPrintLabel` to directly print the label, (b) Add `genInsDisplayName` function to create a string for use when outputting an instruction. For xarch, this prepends the "v" for SIMD instructions, as necessary. This is preferable to calling the raw `genInsName` function, (c) For each insGroup, created a debug-only list of basic blocks that contributed code to that insGroup. Display this set of blocks in the JitDump disasm output, with block ID. This is useful for looking at an IG, and finding the blocks in a .dot flow graph visualization that contributed to it, (d) remove unused `instDisp` 5. Clrjit.natvis: (a) add support for `jitstd::vector`, `JitExpandArray`, `JitExpandArrayStack`, `LcOptInfo`. 6. Misc: (a) When compacting an empty loop preheader block with a subsequent block, clear the preheader flag. ## benchmarks.run.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 25504 Total bytes of diff: 25092 Total bytes of delta: -412 (-1.62% of base) Total relative delta: -0.31 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -92 : 14861.dasm (-2.57% of base) -88 : 2430.dasm (-0.77% of base) -68 : 12182.dasm (-3.82% of base) -48 : 24678.dasm (-1.61% of base) -31 : 21598.dasm (-5.13% of base) -26 : 21601.dasm (-4.57% of base) -21 : 25069.dasm (-7.14% of base) -16 : 14859.dasm (-1.38% of base) -11 : 14862.dasm (-1.35% of base) -6 : 21600.dasm (-1.83% of base) -5 : 25065.dasm (-0.58% of base) 11 total files with Code Size differences (11 improved, 0 regressed), 1 unchanged. Top method improvements (bytes): -92 (-2.57% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -88 (-0.77% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -68 (-3.82% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -48 (-1.61% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -31 (-5.13% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -21 (-7.14% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -16 (-1.38% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -6 (-1.83% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -5 (-0.58% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this Top method improvements (percentages): -21 (-7.14% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -31 (-5.13% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -68 (-3.82% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -92 (-2.57% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -6 (-1.83% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -48 (-1.61% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -16 (-1.38% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -88 (-0.77% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -5 (-0.58% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this 11 total methods with Code Size differences (11 improved, 0 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 38374.96 Total PerfScoreUnits of diff: 37914.07000000001 Total PerfScoreUnits of delta: -460.89 (-1.20% of base) Total relative delta: -0.12 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -220.67 : 24678.dasm (-1.74% of base) -99.27 : 14861.dasm (-2.09% of base) -66.30 : 21598.dasm (-1.41% of base) -18.73 : 2430.dasm (-0.28% of base) -18.40 : 21601.dasm (-1.37% of base) -9.73 : 25065.dasm (-0.56% of base) -9.05 : 14859.dasm (-0.77% of base) -5.51 : 21600.dasm (-0.77% of base) -4.15 : 12182.dasm (-0.17% of base) -3.92 : 14860.dasm (-0.32% of base) -3.46 : 25069.dasm (-2.31% of base) -1.70 : 14862.dasm (-0.20% of base) 12 total files with Perf Score differences (12 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -220.67 (-1.74% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -99.27 (-2.09% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -66.30 (-1.41% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.73 (-0.28% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -18.40 (-1.37% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.73 (-0.56% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this -9.05 (-0.77% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -4.15 (-0.17% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -3.92 (-0.32% of base) : 14860.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -3.46 (-2.31% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -1.70 (-0.20% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) Top method improvements (percentages): -3.46 (-2.31% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -99.27 (-2.09% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -220.67 (-1.74% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -66.30 (-1.41% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.40 (-1.37% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.05 (-0.77% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -9.73 (-0.56% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this -3.92 (-0.32% of base) : 14860.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -18.73 (-0.28% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -1.70 (-0.20% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -4.15 (-0.17% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) 12 total methods with Perf Score differences (12 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ## coreclr_tests.pmi.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 25430 Total bytes of diff: 24994 Total bytes of delta: -436 (-1.71% of base) Total relative delta: -0.42 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -92 : 194668.dasm (-2.57% of base) -68 : 194589.dasm (-3.82% of base) -48 : 248565.dasm (-1.61% of base) -32 : 249053.dasm (-3.58% of base) -31 : 251012.dasm (-5.13% of base) -26 : 251011.dasm (-4.57% of base) -19 : 248561.dasm (-6.76% of base) -16 : 194667.dasm (-1.38% of base) -15 : 252241.dasm (-0.72% of base) -12 : 252242.dasm (-0.81% of base) -11 : 194669.dasm (-1.35% of base) -9 : 246308.dasm (-1.06% of base) -9 : 246307.dasm (-1.06% of base) -9 : 246245.dasm (-1.06% of base) -9 : 246246.dasm (-1.06% of base) -6 : 228622.dasm (-0.77% of base) -6 : 251010.dasm (-1.83% of base) -5 : 248557.dasm (-0.61% of base) -4 : 249054.dasm (-0.50% of base) -4 : 249052.dasm (-0.47% of base) 22 total files with Code Size differences (22 improved, 0 regressed), 1 unchanged. Top method improvements (bytes): -92 (-2.57% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -68 (-3.82% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -48 (-1.61% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -32 (-3.58% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -31 (-5.13% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -19 (-6.76% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -16 (-1.38% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -15 (-0.72% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -12 (-0.81% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -11 (-1.35% of base) : 194669.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -9 (-1.06% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -6 (-0.77% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -6 (-1.83% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -5 (-0.61% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -4 (-0.50% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -4 (-0.47% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() Top method improvements (percentages): -19 (-6.76% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -31 (-5.13% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -68 (-3.82% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -32 (-3.58% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -92 (-2.57% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -6 (-1.83% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -48 (-1.61% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -16 (-1.38% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 194669.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -3 (-1.11% of base) : 249057.dasm - SimpleArray_01.Test:Test2() -9 (-1.06% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -12 (-0.81% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -6 (-0.77% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -15 (-0.72% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -5 (-0.61% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -4 (-0.50% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() 22 total methods with Code Size differences (22 improved, 0 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 161610.68999999997 Total PerfScoreUnits of diff: 160290.10999999996 Total PerfScoreUnits of delta: -1320.58 (-0.82% of base) Total relative delta: -0.20 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -639.25 : 252241.dasm (-0.97% of base) -220.67 : 248565.dasm (-1.74% of base) -132.59 : 252242.dasm (-0.26% of base) -99.27 : 194668.dasm (-2.09% of base) -66.30 : 251012.dasm (-1.41% of base) -62.20 : 249053.dasm (-2.74% of base) -18.40 : 251011.dasm (-1.37% of base) -9.33 : 248557.dasm (-0.54% of base) -9.05 : 194667.dasm (-0.77% of base) -8.32 : 249054.dasm (-0.42% of base) -5.85 : 246308.dasm (-0.52% of base) -5.85 : 246307.dasm (-0.52% of base) -5.85 : 246245.dasm (-0.52% of base) -5.85 : 246246.dasm (-0.52% of base) -5.51 : 251010.dasm (-0.77% of base) -4.36 : 249052.dasm (-0.22% of base) -4.16 : 253363.dasm (-0.21% of base) -4.15 : 194589.dasm (-0.17% of base) -3.92 : 194666.dasm (-0.32% of base) -3.41 : 248561.dasm (-2.29% of base) 23 total files with Perf Score differences (23 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -639.25 (-0.97% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -220.67 (-1.74% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -132.59 (-0.26% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -99.27 (-2.09% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -66.30 (-1.41% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -62.20 (-2.74% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -18.40 (-1.37% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.33 (-0.54% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -9.05 (-0.77% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -8.32 (-0.42% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -5.85 (-0.52% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.51 (-0.77% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -4.36 (-0.22% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() -4.16 (-0.21% of base) : 253363.dasm - MatrixMul.Test:MatrixMul() -4.15 (-0.17% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -3.92 (-0.32% of base) : 194666.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -3.41 (-2.29% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) Top method improvements (percentages): -62.20 (-2.74% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -3.41 (-2.29% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -99.27 (-2.09% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -220.67 (-1.74% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -2.70 (-1.71% of base) : 249057.dasm - SimpleArray_01.Test:Test2() -66.30 (-1.41% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.40 (-1.37% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -639.25 (-0.97% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -9.05 (-0.77% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -9.33 (-0.54% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -5.85 (-0.52% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -8.32 (-0.42% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -3.92 (-0.32% of base) : 194666.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -132.59 (-0.26% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -1.89 (-0.22% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -4.36 (-0.22% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() 23 total methods with Perf Score differences (23 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ## libraries.crossgen2.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 10828 Total bytes of diff: 10809 Total bytes of delta: -19 (-0.18% of base) Total relative delta: -0.00 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -19 : 72504.dasm (-0.18% of base) 1 total files with Code Size differences (1 improved, 0 regressed), 0 unchanged. Top method improvements (bytes): -19 (-0.18% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method improvements (percentages): -19 (-0.18% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 1 total methods with Code Size differences (1 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 6597.12 Total PerfScoreUnits of diff: 6586.31 Total PerfScoreUnits of delta: -10.81 (-0.16% of base) Total relative delta: -0.00 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -10.81 : 72504.dasm (-0.16% of base) 1 total files with Perf Score differences (1 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -10.81 (-0.16% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method improvements (percentages): -10.81 (-0.16% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 1 total methods with Perf Score differences (1 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- * Increase loop cloning max allowed condition blocks Allows inner loop of 3-nested loops (e.g., Array2 benchmark) to be cloned. * Clear GTF_INX_RNGCHK bit on loop cloning created index nodes to avoid unnecessary bounds checks. Revert max cloning condition blocks to 3; allowing more doesn't seem to improve performance (probably too many conditions before a not-sufficiently-executed loop, at least for the Array2 benchmark) * Remove outer index bounds checks * Convert loop cloning data structures to `vector` for better debugging * Improve CSE dump output 1. "#if 0" the guts of the CSE dataflow; that's not useful to most people. 2. Add readable CSE number output to the CSE dataflow set output 3. Add FMT_CSE to commonize CSE number output. 4. Add PHASE_OPTIMIZE_VALNUM_CSES to the pre-phase output "allow list" and stop doing its own blocks/trees output. 5. Remove unused optCSECandidateTotal 6. Add functions `getCSEAvailBit` and `getCSEAvailCrossCallBit` to avoid hand-coding these bit calculations in multiple places, for the CSE dataflow set bits. * Mark cloned array indexes as non-faulting When generating loop cloning conditions, mark array index expressions as non-faulting, as we have already null- and range-checked the array before generating an index expression. I also added similary code to mark array length expressions as non-faulting, for the same reason. However, that leads to CQ losses because of downstream CSE effects. * Don't count zero-sized align instructions in PerfScore * Add COMPlus_JitDasmWithAlignmentBoundaries This outputs the alignment boundaries without requiring outputting the actual addresses. It makes it easier to diff changes. * Improve bounds check output * Improve emitter label printing Create function for printing bound emitter labels. Also, add debug code to associate a BasicBlock with an insGroup, and output the block number and ID with the emitter label in JitDump, so it's easier to find where a group of generated instructions came from. * Formatting * Clear BBF_LOOP_PREHEADER bit when compacting empty pre-header block * Keep track of all basic blocks that contribute code to an insGroup * Update display of Intel jcc erratum branches in dump For instructions or instruction sequences which match the Intel jcc erratum criteria, note that in the alignment boundary dump. Also, a few fixes: 1. Move the alignment boundary dumping from `emitIssue1Instr` to `emitEndCodeGen` to avoid the possibility of reading the next instruction in a group when there is no next instruction. 2. Create `IsJccInstruction` and `IsJmpInstruction` functions for use by the jcc criteria detection, and fix that detection to fix a few omissions/errors. 3. Change the jcc criteria detection to be hard-coded to 32 byte boundaries instead of assuming `compJitAlignLoopBoundary` is 32. An example: ``` cmp r11d, dword ptr [rax+8] ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (cmp: 0 ; jcc erratum) 32B boundary ............................... jae G_M42486_IG103 ``` In this case, the `cmp` doesn't cross the boundary, it is adjacent (the zero indicates the number of bytes of the instruction which cross the boundary), followed by the `jae` which starts after the boundary. Indicating the jcc erratum criteria can help point out potential performance issues due to unlucky alignment of these instructions in asm diffs. * Display full instruction name in alignment and other messages XArch sometimes prepends a "v" to the instructions names from the instruction table. Add a function `genInsDisplayName` to create the full instruction name that should be displayed, and use that in most places an instruction name will be displayed, such as in the alignment messages, and normal disassembly. Use this instead of the raw `genInsName`. This could be extended to handle arm32 appending an "s", but I didn't want to touch arm32 with this change. * Fix build * Code review feedback 1. Rename GTF_INX_NONFAULTING to GTF_INX_NOFAULT to increase clarity compared to existing GTF_IND_NONFAULTING. 2. Minor cleanup in getInsDisplayName. * Formatting --- src/coreclr/jit/block.h | 5 +- src/coreclr/jit/clrjit.natvis | 51 +++++ src/coreclr/jit/codegen.h | 5 +- src/coreclr/jit/codegencommon.cpp | 7 +- src/coreclr/jit/codegenlinear.cpp | 2 +- src/coreclr/jit/compiler.cpp | 6 + src/coreclr/jit/compiler.h | 65 +++++-- src/coreclr/jit/compiler.hpp | 19 -- src/coreclr/jit/emit.cpp | 296 +++++++++++++++++++---------- src/coreclr/jit/emit.h | 14 +- src/coreclr/jit/emitarm.cpp | 6 +- src/coreclr/jit/emitarm64.cpp | 8 +- src/coreclr/jit/emitxarch.cpp | 85 ++++++--- src/coreclr/jit/emitxarch.h | 18 +- src/coreclr/jit/fgopt.cpp | 3 + src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/instr.cpp | 63 +++--- src/coreclr/jit/jitconfigvalues.h | 3 + src/coreclr/jit/jitstd/algorithm.h | 4 +- src/coreclr/jit/loopcloning.cpp | 184 ++++++++++++++---- src/coreclr/jit/loopcloning.h | 45 ++--- src/coreclr/jit/morph.cpp | 11 +- src/coreclr/jit/optcse.cpp | 186 ++++++++++-------- src/coreclr/jit/optimizer.cpp | 9 +- src/coreclr/jit/phase.cpp | 5 +- 26 files changed, 730 insertions(+), 373 deletions(-) diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 0cbe51281b353..5d42a1626536f 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -29,7 +29,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jithashtable.h" /*****************************************************************************/ -typedef BitVec EXPSET_TP; +typedef BitVec EXPSET_TP; +typedef BitVec_ValArg_T EXPSET_VALARG_TP; +typedef BitVec_ValRet_T EXPSET_VALRET_TP; + #if LARGE_EXPSET #define EXPSET_SZ 64 #else diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis index 90a9ff703a471..b3c187474900f 100644 --- a/src/coreclr/jit/clrjit.natvis +++ b/src/coreclr/jit/clrjit.natvis @@ -5,6 +5,13 @@ Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. --> + @@ -183,4 +190,48 @@ The .NET Foundation licenses this file to you under the MIT license. + + size={m_nSize,d} capacity={m_nCapacity,d} + Empty + + + m_nSize + m_pArray + + + + + + size={m_size,d} + Empty + + + m_size + m_members + + + + + + size={m_size,d} used={m_used,d} + Empty + + + m_used + m_members + + + + + + + + + {optType,en} + + (LcJaggedArrayOptInfo*)this,nd + (LcMdArrayOptInfo*)this,nd + + + diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 132a763c06b01..626cb3e5b7bd6 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -216,6 +216,7 @@ class CodeGen final : public CodeGenInterface unsigned genCurDispOffset; static const char* genInsName(instruction ins); + const char* genInsDisplayName(emitter::instrDesc* id); #endif // DEBUG //------------------------------------------------------------------------- @@ -1503,10 +1504,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void instGen_Store_Reg_Into_Lcl(var_types dstType, regNumber srcReg, int varNum, int offs); -#ifdef DEBUG - void __cdecl instDisp(instruction ins, bool noNL, const char* fmt, ...); -#endif - #ifdef TARGET_XARCH instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue); #endif // TARGET_XARCH diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index aeecb0553b989..d2e08159ec104 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1070,7 +1070,7 @@ void CodeGen::genDefineTempLabel(BasicBlock* label) { genLogLabel(label); label->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label->bbNum)); + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label)); } // genDefineInlineTempLabel: Define an inline label that does not affect the GC @@ -2064,9 +2064,8 @@ void CodeGen::genInsertNopForUnwinder(BasicBlock* block) // block starts an EH region. If we pointed the existing bbEmitCookie here, then the NOP // would be executed, which we would prefer not to do. - block->bbUnwindNopEmitCookie = - GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - false DEBUG_ARG(block->bbNum)); + block->bbUnwindNopEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block)); instGen(INS_nop); } diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index b822f233e66db..aa2fb0f58955f 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -356,7 +356,7 @@ void CodeGen::genCodeForBBlist() // Mark a label and update the current set of live GC refs block->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block->bbNum)); + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block)); } if (block == compiler->fgFirstColdBlock) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7019d8afad6c5..bf49e63ab0cbb 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2988,6 +2988,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.disAsmSpilled = false; opts.disDiffable = false; opts.disAddr = false; + opts.disAlignment = false; opts.dspCode = false; opts.dspEHTable = false; opts.dspDebugInfo = false; @@ -3136,6 +3137,11 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.disAddr = true; } + if (JitConfig.JitDasmWithAlignmentBoundaries() != 0) + { + opts.disAlignment = true; + } + if (JitConfig.JitLongAddress() != 0) { opts.compLongAddress = true; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1383b57a7dd3a..b60353d8e8440 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6237,9 +6237,9 @@ class Compiler public: void optInit(); - GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt); - GenTree* Compiler::optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt); - void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt); + GenTree* optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt); + GenTree* optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt); + void optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt); bool optIsRangeCheckRemovable(GenTree* tree); protected: @@ -6728,7 +6728,7 @@ class Compiler // BitVec trait information for computing CSE availability using the CSE_DataFlow algorithm. // Two bits are allocated per CSE candidate to compute CSE availability // plus an extra bit to handle the initial unvisited case. - // (See CSE_DataFlow::EndMerge for an explaination of why this is necessary) + // (See CSE_DataFlow::EndMerge for an explanation of why this is necessary.) // // The two bits per CSE candidate have the following meanings: // 11 - The CSE is available, and is also available when considering calls as killing availability. @@ -6738,6 +6738,37 @@ class Compiler // BitVecTraits* cseLivenessTraits; + //----------------------------------------------------------------------------------------------------------------- + // getCSEnum2bit: Return the normalized index to use in the EXPSET_TP for the CSE with the given CSE index. + // Each GenTree has a `gtCSEnum` field. Zero is reserved to mean this node is not a CSE, positive values indicate + // CSE uses, and negative values indicate CSE defs. The caller must pass a non-zero positive value, as from + // GET_CSE_INDEX(). + // + static unsigned genCSEnum2bit(unsigned CSEnum) + { + assert((CSEnum > 0) && (CSEnum <= MAX_CSE_CNT)); + return CSEnum - 1; + } + + //----------------------------------------------------------------------------------------------------------------- + // getCSEAvailBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit for a CSE. + // + static unsigned getCSEAvailBit(unsigned CSEnum) + { + return genCSEnum2bit(CSEnum) * 2; + } + + //----------------------------------------------------------------------------------------------------------------- + // getCSEAvailCrossCallBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit + // for a CSE considering calls as killing availability bit (see description above). + // + static unsigned getCSEAvailCrossCallBit(unsigned CSEnum) + { + return getCSEAvailBit(CSEnum) + 1; + } + + void optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits = true); + EXPSET_TP cseCallKillsMask; // Computed once - A mask that is used to kill available CSEs at callsites /* Generic list of nodes - used by the CSE logic */ @@ -6872,9 +6903,12 @@ class Compiler return (enckey & ~TARGET_SIGN_BIT) << CSE_CONST_SHARED_LOW_BITS; } - /************************************************************************** - * Value Number based CSEs - *************************************************************************/ +/************************************************************************** + * Value Number based CSEs + *************************************************************************/ + +// String to use for formatting CSE numbers. Note that this is the positive number, e.g., from GET_CSE_INDEX(). +#define FMT_CSE "CSE #%02u" public: void optOptimizeValnumCSEs(); @@ -6882,16 +6916,15 @@ class Compiler protected: void optValnumCSE_Init(); unsigned optValnumCSE_Index(GenTree* tree, Statement* stmt); - unsigned optValnumCSE_Locate(); - void optValnumCSE_InitDataFlow(); - void optValnumCSE_DataFlow(); - void optValnumCSE_Availablity(); - void optValnumCSE_Heuristic(); + bool optValnumCSE_Locate(); + void optValnumCSE_InitDataFlow(); + void optValnumCSE_DataFlow(); + void optValnumCSE_Availablity(); + void optValnumCSE_Heuristic(); bool optDoCSE; // True when we have found a duplicate CSE tree - bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase - unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum - unsigned optCSECandidateCount; // Count of CSE's candidates, reset for Lexical and ValNum CSE's + bool optValnumCSE_phase; // True when we are executing the optOptimizeValnumCSEs() phase + unsigned optCSECandidateCount; // Count of CSE's candidates unsigned optCSEstart; // The first local variable number that is a CSE unsigned optCSEcount; // The total count of CSE's introduced. BasicBlock::weight_t optCSEweight; // The weight of the current block when we are doing PerformCSE @@ -6916,6 +6949,7 @@ class Compiler bool optConfigDisableCSE(); bool optConfigDisableCSE2(); #endif + void optOptimizeCSEs(); struct isVarAssgDsc @@ -9337,6 +9371,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX bool disasmWithGC; // Display GC info interleaved with disassembly. bool disDiffable; // Makes the Disassembly code 'diff-able' bool disAddr; // Display process address next to each instruction in disassembly code + bool disAlignment; // Display alignment boundaries in disassembly code bool disAsm2; // Display native code after it is generated using external disassembler bool dspOrder; // Display names of each of the methods that we ngen/jit bool dspUnwind; // Display the unwind info output diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index d05bfc6bbeb9f..ca9626ee11c1f 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -763,25 +763,6 @@ inline double getR8LittleEndian(const BYTE* ptr) return *(double*)&val; } -/***************************************************************************** - * - * Return the normalized index to use in the EXPSET_TP for the CSE with - * the given CSE index. - * Each GenTree has the following field: - * signed char gtCSEnum; // 0 or the CSE index (negated if def) - * So zero is reserved to mean this node is not a CSE - * and postive values indicate CSE uses and negative values indicate CSE defs. - * The caller of this method must pass a non-zero postive value. - * This precondition is checked by the assert on the first line of this method. - */ - -inline unsigned int genCSEnum2bit(unsigned index) -{ - assert((index > 0) && (index <= EXPSET_SZ)); - - return (index - 1); -} - #ifdef DEBUG const char* genES2str(BitVecTraits* traits, EXPSET_TP set); const char* refCntWtd2str(BasicBlock::weight_t refCntWtd); diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 913951568fd1b..ca6cd0c4a7133 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -773,7 +773,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) memcpy(id, emitCurIGfreeBase, sz); #ifdef DEBUG - if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false) + if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false)) { // If there's an error during emission, we may want to connect the post-copy address // of an instrDesc with the pre-copy address (the one that was originally created). This @@ -843,7 +843,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) #ifdef DEBUG if (emitComp->opts.dspCode) { - printf("\n G_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum); + printf("\n %s:", emitLabelString(ig)); if (emitComp->verbose) { printf(" ; offs=%06XH, funclet=%02u, bbWeight=%s", ig->igOffs, ig->igFuncIdx, @@ -1184,7 +1184,7 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const //---------------------------------------------------------------------------------------- // insEvaluateExecutionCost: -// Returns the estimate execution cost fortyhe current instruction +// Returns the estimated execution cost for the current instruction // // Arguments: // id - The current instruction descriptor to be evaluated @@ -1193,8 +1193,6 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const // calls getInsExecutionCharacteristics and uses the result // to compute an estimated execution cost // -// Notes: -// float emitter::insEvaluateExecutionCost(instrDesc* id) { insExecutionCharacteristics result = getInsExecutionCharacteristics(id); @@ -1202,8 +1200,10 @@ float emitter::insEvaluateExecutionCost(instrDesc* id) float latency = result.insLatency; unsigned memAccessKind = result.insMemoryAccessKind; - // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL - assert(throughput > 0.0); + // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL. + // Note that 0.0 throughput is allowed for pseudo-instructions in the instrDesc list that won't actually + // generate code. + assert(throughput >= 0.0); assert(latency >= 0.0); if (memAccessKind == PERFSCORE_MEMORY_WRITE) @@ -1241,7 +1241,7 @@ float emitter::insEvaluateExecutionCost(instrDesc* id) void emitter::perfScoreUnhandledInstruction(instrDesc* id, insExecutionCharacteristics* pResult) { #ifdef DEBUG - printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsName(id->idIns()), + printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsDisplayName(id), emitIfName(id->idInsFmt())); assert(!"PerfScore: unhandled instruction"); #endif @@ -1524,6 +1524,14 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) emitCurIGinsCnt++; +#ifdef DEBUG + if (emitComp->compCurBB != emitCurIG->lastGeneratedBlock) + { + emitCurIG->igBlocks.push_back(emitComp->compCurBB); + emitCurIG->lastGeneratedBlock = emitComp->compCurBB; + } +#endif // DEBUG + return id; } @@ -2504,7 +2512,7 @@ bool emitter::emitNoGChelper(CORINFO_METHOD_HANDLE methHnd) void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - bool isFinallyTarget DEBUG_ARG(unsigned bbNum)) + bool isFinallyTarget DEBUG_ARG(BasicBlock* block)) { /* Create a new IG if the current one is non-empty */ @@ -2533,7 +2541,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, #endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) #ifdef DEBUG - JITDUMP("Mapped " FMT_BB " to G_M%03u_IG%02u\n", bbNum, emitComp->compMethodID, emitCurIG->igNum); + JITDUMP("Mapped " FMT_BB " to %s\n", block->bbNum, emitLabelString(emitCurIG)); if (EMIT_GC_VERBOSE) { @@ -2548,6 +2556,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, printf("\n"); } #endif + return emitCurIG; } @@ -2561,6 +2570,40 @@ void* emitter::emitAddInlineLabel() return emitCurIG; } +#ifdef DEBUG + +//----------------------------------------------------------------------------- +// emitPrintLabel: Print the assembly label for an insGroup. We could use emitter::emitLabelString() +// to be consistent, but that seems silly. +// +void emitter::emitPrintLabel(insGroup* ig) +{ + printf("G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); +} + +//----------------------------------------------------------------------------- +// emitLabelString: Return label string for an insGroup, for use in debug output. +// This can be called up to four times in a single 'printf' before the static buffers +// get reused. +// +// Returns: +// String with insGroup label +// +const char* emitter::emitLabelString(insGroup* ig) +{ + const int TEMP_BUFFER_LEN = 40; + static unsigned curBuf = 0; + static char buf[4][TEMP_BUFFER_LEN]; + const char* retbuf; + + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); + retbuf = buf[curBuf]; + curBuf = (curBuf + 1) % 4; + return retbuf; +} + +#endif // DEBUG + #ifdef TARGET_ARMARCH // Does the argument location point to an IG at the end of a function or funclet? @@ -3502,7 +3545,7 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) const int TEMP_BUFFER_LEN = 40; char buff[TEMP_BUFFER_LEN]; - sprintf_s(buff, TEMP_BUFFER_LEN, "G_M%03u_IG%02u: ", emitComp->compMethodID, ig->igNum); + sprintf_s(buff, TEMP_BUFFER_LEN, "%s: ", emitLabelString(ig)); printf("%s; ", buff); // We dump less information when we're only interleaving GC info with a disassembly listing, @@ -3599,9 +3642,10 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) else { const char* separator = ""; + if (jitdump) { - printf("offs=%06XH, size=%04XH", ig->igOffs, ig->igSize); + printf("%soffs=%06XH, size=%04XH", separator, ig->igOffs, ig->igSize); separator = ", "; } @@ -3642,6 +3686,15 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) } #endif // FEATURE_LOOP_ALIGN + if (jitdump && !ig->igBlocks.empty()) + { + for (auto block : ig->igBlocks) + { + printf("%s%s", separator, block->dspToString()); + separator = ", "; + } + } + emitDispIGflags(ig->igFlags); if (ig == emitCurIG) @@ -3733,10 +3786,6 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) { size_t is; -#ifdef DEBUG - size_t beforeAddr = (size_t)*dp; -#endif - /* Record the beginning offset of the instruction */ BYTE* curInsAdr = *dp; @@ -3822,52 +3871,7 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) id->idDebugOnlyInfo()->idNum, is, emitSizeOfInsDsc(id)); assert(is == emitSizeOfInsDsc(id)); } - - // Print the alignment boundary - if ((emitComp->opts.disAsm || emitComp->verbose) && emitComp->opts.disAddr) - { - size_t currAddr = (size_t)*dp; - size_t lastBoundaryAddr = currAddr & ~((size_t)emitComp->opts.compJitAlignLoopBoundary - 1); - - // draw boundary if beforeAddr was before the lastBoundary. - if (beforeAddr < lastBoundaryAddr) - { - printf("; "); - instruction currIns = id->idIns(); - -#if defined(TARGET_XARCH) - - // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf - bool isJccAffectedIns = - ((currIns >= INS_i_jmp && currIns < INS_align) || (currIns == INS_call) || (currIns == INS_ret)); - - instrDesc* nextId = id; - castto(nextId, BYTE*) += is; - instruction nextIns = nextId->idIns(); - if ((currIns == INS_cmp) || (currIns == INS_test) || (currIns == INS_add) || (currIns == INS_sub) || - (currIns == INS_and) || (currIns == INS_inc) || (currIns == INS_dec)) - { - isJccAffectedIns |= (nextIns >= INS_i_jmp && nextIns < INS_align); - } -#else - bool isJccAffectedIns = false; -#endif - - // Indicate if instruction is at at 32B boundary or is splitted - unsigned bytesCrossedBoundary = (currAddr & (emitComp->opts.compJitAlignLoopBoundary - 1)); - if ((bytesCrossedBoundary != 0) || (isJccAffectedIns && bytesCrossedBoundary == 0)) - { - printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)", codeGen->genInsName(id->idIns()), - bytesCrossedBoundary); - } - else - { - printf("..............................."); - } - printf(" %dB boundary ...............................\n", (emitComp->opts.compJitAlignLoopBoundary)); - } - } -#endif +#endif // DEBUG return is; } @@ -4229,7 +4233,7 @@ void emitter::emitJumpDistBind() { if (tgtIG) { - printf(" to G_M%03u_IG%02u\n", emitComp->compMethodID, tgtIG->igNum); + printf(" to %s\n", emitLabelString(tgtIG)); } else { @@ -4687,8 +4691,7 @@ void emitter::emitLoopAlignment() // all IGs that follows this IG and participate in a loop. emitCurIG->igFlags |= IGF_LOOP_ALIGN; - JITDUMP("Adding 'align' instruction of %d bytes in G_M%03u_IG%02u.\n", paddingBytes, emitComp->compMethodID, - emitCurIG->igNum); + JITDUMP("Adding 'align' instruction of %d bytes in %s.\n", paddingBytes, emitLabelString(emitCurIG)); #ifdef DEBUG emitComp->loopAlignCandidates++; @@ -5027,10 +5030,10 @@ void emitter::emitLoopAlignAdjustments() alignInstr = prevAlignInstr; } - JITDUMP("Adjusted alignment of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, - estimatedPaddingNeeded, actualPaddingNeeded); - JITDUMP("Adjusted size of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, - (alignIG->igSize + diff), alignIG->igSize); + JITDUMP("Adjusted alignment of %s from %u to %u.\n", emitLabelString(alignIG), estimatedPaddingNeeded, + actualPaddingNeeded); + JITDUMP("Adjusted size of %s from %u to %u.\n", emitLabelString(alignIG), (alignIG->igSize + diff), + alignIG->igSize); } // Adjust the offset of all IGs starting from next IG until we reach the IG having the next @@ -5039,8 +5042,8 @@ void emitter::emitLoopAlignAdjustments() insGroup* adjOffUptoIG = alignInstr->idaNext != nullptr ? alignInstr->idaNext->idaIG : emitIGlast; while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum)) { - JITDUMP("Adjusted offset of G_M%03u_IG%02u from %04X to %04X\n", emitComp->compMethodID, adjOffIG->igNum, - adjOffIG->igOffs, (adjOffIG->igOffs - alignBytesRemoved)); + JITDUMP("Adjusted offset of %s from %04X to %04X\n", emitLabelString(adjOffIG), adjOffIG->igOffs, + (adjOffIG->igOffs - alignBytesRemoved)); adjOffIG->igOffs -= alignBytesRemoved; adjOffIG = adjOffIG->igNext; } @@ -5099,8 +5102,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs // No padding if loop is already aligned if ((offset & (alignmentBoundary - 1)) == 0) { - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %dB boundary.'\n", - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %dB boundary.'\n", emitLabelString(ig->igNext), + alignmentBoundary); return 0; } @@ -5124,8 +5127,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs // No padding if loop is big if (loopSize > maxLoopSize) { - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is big. LoopSize= %d, MaxLoopSize= %d.'\n", - emitComp->compMethodID, ig->igNext->igNum, loopSize, maxLoopSize); + JITDUMP(";; Skip alignment: 'Loop at %s is big. LoopSize= %d, MaxLoopSize= %d.'\n", emitLabelString(ig->igNext), + loopSize, maxLoopSize); return 0; } @@ -5151,17 +5154,16 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs if (nPaddingBytes == 0) { skipPadding = true; - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %uB boundary.'\n", - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %uB boundary.'\n", + emitLabelString(ig->igNext), alignmentBoundary); } // Check if the alignment exceeds new maxPadding limit else if (nPaddingBytes > nMaxPaddingBytes) { skipPadding = true; - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, " + JITDUMP(";; Skip alignment: 'Loop at %s PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, " "AlignmentBoundary= %dB.'\n", - emitComp->compMethodID, ig->igNext->igNum, nPaddingBytes, nMaxPaddingBytes, loopSize, - alignmentBoundary); + emitLabelString(ig->igNext), nPaddingBytes, nMaxPaddingBytes, loopSize, alignmentBoundary); } } @@ -5183,8 +5185,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs else { // Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment. - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n", - emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n", + emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary); } } } @@ -5212,13 +5214,13 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs else { // Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment. - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n", - emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n", + emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary); } } - JITDUMP(";; Calculated padding to add %d bytes to align G_M%03u_IG%02u at %dB boundary.\n", paddingToAdd, - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Calculated padding to add %d bytes to align %s at %dB boundary.\n", paddingToAdd, + emitLabelString(ig->igNext), alignmentBoundary); // Either no padding is added because it is too expensive or the offset gets aligned // to the alignment boundary @@ -5900,7 +5902,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, } else { - printf("\nG_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum); + printf("\n%s:", emitLabelString(ig)); if (!emitComp->opts.disDiffable) { printf(" ;; offset=%04XH", emitCurCodeOffs(cp)); @@ -6017,9 +6019,97 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, emitCurIG = ig; - for (unsigned cnt = ig->igInsCnt; cnt; cnt--) + for (unsigned cnt = ig->igInsCnt; cnt > 0; cnt--) { +#ifdef DEBUG + size_t curInstrAddr = (size_t)cp; + instrDesc* curInstrDesc = id; +#endif + castto(id, BYTE*) += emitIssue1Instr(ig, id, &cp); + +#ifdef DEBUG + // Print the alignment boundary + if ((emitComp->opts.disAsm || emitComp->verbose) && (emitComp->opts.disAddr || emitComp->opts.disAlignment)) + { + size_t afterInstrAddr = (size_t)cp; + instruction curIns = curInstrDesc->idIns(); + bool isJccAffectedIns = false; + +#if defined(TARGET_XARCH) + + // Determine if this instruction is part of a set that matches the Intel jcc erratum characteristic + // described here: + // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf + // This is the case when a jump instruction crosses a 32-byte boundary, or ends on a 32-byte boundary. + // "Jump instruction" in this case includes conditional jump (jcc), macro-fused op-jcc (where 'op' is + // one of cmp, test, add, sub, and, inc, or dec), direct unconditional jump, indirect jump, + // direct/indirect call, and return. + + size_t jccAlignBoundary = 32; + size_t jccAlignBoundaryMask = jccAlignBoundary - 1; + size_t jccLastBoundaryAddr = afterInstrAddr & ~jccAlignBoundaryMask; + + if (curInstrAddr < jccLastBoundaryAddr) + { + isJccAffectedIns = IsJccInstruction(curIns) || IsJmpInstruction(curIns) || (curIns == INS_call) || + (curIns == INS_ret); + + // For op-Jcc there are two cases: (1) curIns is the jcc, in which case the above condition + // already covers us. (2) curIns is the `op` and the next instruction is the `jcc`. Note that + // we will never have a `jcc` as the first instruction of a group, so we don't need to worry + // about looking ahead to the next group after a an `op` of `op-Jcc`. + + if (!isJccAffectedIns && (cnt > 1)) + { + // The current `id` is valid, namely, there is another instruction in this group. + instruction nextIns = id->idIns(); + if (((curIns == INS_cmp) || (curIns == INS_test) || (curIns == INS_add) || + (curIns == INS_sub) || (curIns == INS_and) || (curIns == INS_inc) || + (curIns == INS_dec)) && + IsJccInstruction(nextIns)) + { + isJccAffectedIns = true; + } + } + + if (isJccAffectedIns) + { + unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & jccAlignBoundaryMask); + printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d ; jcc erratum) %dB boundary " + "...............................\n", + codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary, jccAlignBoundary); + } + } + +#endif // TARGET_XARCH + + // Jcc affected instruction boundaries were printed above; handle other cases here. + if (!isJccAffectedIns) + { + size_t alignBoundaryMask = (size_t)emitComp->opts.compJitAlignLoopBoundary - 1; + size_t lastBoundaryAddr = afterInstrAddr & ~alignBoundaryMask; + + // draw boundary if beforeAddr was before the lastBoundary. + if (curInstrAddr < lastBoundaryAddr) + { + // Indicate if instruction is at the alignment boundary or is split + unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & alignBoundaryMask); + if (bytesCrossedBoundary != 0) + { + printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)", + codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary); + } + else + { + printf("; ..............................."); + } + printf(" %dB boundary ...............................\n", + emitComp->opts.compJitAlignLoopBoundary); + } + } + } +#endif // DEBUG } #ifdef DEBUG @@ -6862,11 +6952,8 @@ void emitter::emitDispDataSec(dataSecDsc* section) BasicBlock* block = reinterpret_cast(data->dsCont)[i]; insGroup* ig = static_cast(emitCodeGetCookie(block)); - const char* blockLabelFormat = "G_M%03u_IG%02u"; - char blockLabel[64]; - char firstLabel[64]; - sprintf_s(blockLabel, _countof(blockLabel), blockLabelFormat, emitComp->compMethodID, ig->igNum); - sprintf_s(firstLabel, _countof(firstLabel), blockLabelFormat, emitComp->compMethodID, igFirst->igNum); + const char* blockLabel = emitLabelString(ig); + const char* firstLabel = emitLabelString(igFirst); if (isRelative) { @@ -7986,11 +8073,6 @@ insGroup* emitter::emitAllocIG() ig->igSelf = ig; #endif -#if defined(DEBUG) || defined(LATE_DISASM) - ig->igWeight = getCurrentBlockWeight(); - ig->igPerfScore = 0.0; -#endif - #if EMITTER_STATS emitTotalIGcnt += 1; emitTotalIGsize += sz; @@ -8028,6 +8110,11 @@ void emitter::emitInitIG(insGroup* ig) ig->igFlags = 0; +#if defined(DEBUG) || defined(LATE_DISASM) + ig->igWeight = getCurrentBlockWeight(); + ig->igPerfScore = 0.0; +#endif + /* Zero out some fields to avoid printing garbage in JitDumps. These really only need to be set in DEBUG, but do it in all cases to make sure we act the same in non-DEBUG builds. @@ -8040,6 +8127,12 @@ void emitter::emitInitIG(insGroup* ig) #if FEATURE_LOOP_ALIGN ig->igLoopBackEdge = nullptr; #endif + +#ifdef DEBUG + ig->lastGeneratedBlock = nullptr; + // Explicitly call the constructor, since IGs don't actually have a constructor. + ig->igBlocks.jitstd::list::list(emitComp->getAllocator(CMK_LoopOpt)); +#endif } /***************************************************************************** @@ -8104,6 +8197,11 @@ void emitter::emitNxtIG(bool extend) // We've created a new IG; no need to force another one. emitForceNewIG = false; + +#ifdef DEBUG + // We haven't written any code into the IG yet, so clear our record of the last block written to the IG. + emitCurIG->lastGeneratedBlock = nullptr; +#endif } /***************************************************************************** @@ -8638,7 +8736,7 @@ const char* emitter::emitOffsetToLabel(unsigned offs) if (ig->igOffs == offs) { // Found it! - sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "%s", emitLabelString(ig)); retbuf = buf[curBuf]; curBuf = (curBuf + 1) % 4; return retbuf; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 2748acf463766..ef67148ea962a 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -247,6 +247,11 @@ struct insGroup double igPerfScore; // The PerfScore for this insGroup #endif +#ifdef DEBUG + BasicBlock* lastGeneratedBlock; // The last block that generated code into this insGroup. + jitstd::list igBlocks; // All the blocks that generated code into this insGroup. +#endif + UNATIVE_OFFSET igNum; // for ordering (and display) purposes UNATIVE_OFFSET igOffs; // offset of this group within method unsigned int igFuncIdx; // Which function/funclet does this belong to? (Index into Compiler::compFuncInfos array.) @@ -1229,6 +1234,8 @@ class emitter #define PERFSCORE_THROUGHPUT_ILLEGAL -1024.0f +#define PERFSCORE_THROUGHPUT_ZERO 0.0f // Only used for pseudo-instructions that don't generate code + #define PERFSCORE_THROUGHPUT_6X (1.0f / 6.0f) // Hextuple issue #define PERFSCORE_THROUGHPUT_5X 0.20f // Pentuple issue #define PERFSCORE_THROUGHPUT_4X 0.25f // Quad issue @@ -1902,13 +1909,18 @@ class emitter void* emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - bool isFinallyTarget = false DEBUG_ARG(unsigned bbNum = 0)); + bool isFinallyTarget = false DEBUG_ARG(BasicBlock* block = nullptr)); // Same as above, except the label is added and is conceptually "inline" in // the current block. Thus it extends the previous block and the emitter // continues to track GC info as if there was no label. void* emitAddInlineLabel(); +#ifdef DEBUG + void emitPrintLabel(insGroup* ig); + const char* emitLabelString(insGroup* ig); +#endif + #ifdef TARGET_ARMARCH void emitGetInstrDescs(insGroup* ig, instrDesc** id, int* insCnt); diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 6953df5b49a7c..5b01adbd14a5d 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -7292,7 +7292,7 @@ void emitter::emitDispInsHelp( lab = (insGroup*)emitCodeGetCookie(*bbp++); assert(lab); - printf("\n DD G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum); + printf("\n DD %s", emitLabelString(lab)); } while (--cnt); } } @@ -7601,7 +7601,7 @@ void emitter::emitDispInsHelp( case IF_T2_M1: // Load Label emitDispReg(id->idReg1(), attr, true); if (id->idIsBound()) - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); else printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); break; @@ -7646,7 +7646,7 @@ void emitter::emitDispInsHelp( } } else if (id->idIsBound()) - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); else printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); } diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 37e19a219d265..84c49d94723d4 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -12286,7 +12286,7 @@ void emitter::emitDispIns( } else if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12324,7 +12324,7 @@ void emitter::emitDispIns( emitDispReg(id->idReg1(), size, true); if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12338,7 +12338,7 @@ void emitter::emitDispIns( emitDispImm(emitGetInsSC(id), true); if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12463,7 +12463,7 @@ void emitter::emitDispIns( } else if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 217f5f15aafb2..c35a6675fe757 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -24,37 +24,37 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "emit.h" #include "codegen.h" -bool IsSSEInstruction(instruction ins) +bool emitter::IsSSEInstruction(instruction ins) { return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_SSE_INSTRUCTION); } -bool IsSSEOrAVXInstruction(instruction ins) +bool emitter::IsSSEOrAVXInstruction(instruction ins) { return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } -bool IsAVXOnlyInstruction(instruction ins) +bool emitter::IsAVXOnlyInstruction(instruction ins) { return (ins >= INS_FIRST_AVX_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } -bool IsFMAInstruction(instruction ins) +bool emitter::IsFMAInstruction(instruction ins) { return (ins >= INS_FIRST_FMA_INSTRUCTION) && (ins <= INS_LAST_FMA_INSTRUCTION); } -bool IsAVXVNNIInstruction(instruction ins) +bool emitter::IsAVXVNNIInstruction(instruction ins) { return (ins >= INS_FIRST_AVXVNNI_INSTRUCTION) && (ins <= INS_LAST_AVXVNNI_INSTRUCTION); } -bool IsBMIInstruction(instruction ins) +bool emitter::IsBMIInstruction(instruction ins) { return (ins >= INS_FIRST_BMI_INSTRUCTION) && (ins <= INS_LAST_BMI_INSTRUCTION); } -regNumber getBmiRegNumber(instruction ins) +regNumber emitter::getBmiRegNumber(instruction ins) { switch (ins) { @@ -81,7 +81,7 @@ regNumber getBmiRegNumber(instruction ins) } } -regNumber getSseShiftRegNumber(instruction ins) +regNumber emitter::getSseShiftRegNumber(instruction ins) { switch (ins) { @@ -123,7 +123,7 @@ regNumber getSseShiftRegNumber(instruction ins) } } -bool emitter::IsAVXInstruction(instruction ins) +bool emitter::IsAVXInstruction(instruction ins) const { return UseVEXEncoding() && IsSSEOrAVXInstruction(ins); } @@ -445,7 +445,7 @@ bool emitter::Is4ByteSSEInstruction(instruction ins) // Returns true if this instruction requires a VEX prefix // All AVX instructions require a VEX prefix -bool emitter::TakesVexPrefix(instruction ins) +bool emitter::TakesVexPrefix(instruction ins) const { // special case vzeroupper as it requires 2-byte VEX prefix // special case the fencing, movnti and the prefetch instructions as they never take a VEX prefix @@ -521,7 +521,7 @@ emitter::code_t emitter::AddVexPrefix(instruction ins, code_t code, emitAttr att } // Returns true if this instruction, for the given EA_SIZE(attr), will require a REX.W prefix -bool TakesRexWPrefix(instruction ins, emitAttr attr) +bool emitter::TakesRexWPrefix(instruction ins, emitAttr attr) { // Because the current implementation of AVX does not have a way to distinguish between the register // size specification (128 vs. 256 bits) and the operand size specification (32 vs. 64 bits), where both are @@ -4299,6 +4299,38 @@ bool emitter::IsMovInstruction(instruction ins) } } +//------------------------------------------------------------------------ +// IsJccInstruction: Determine if an instruction is a conditional jump instruction. +// +// Arguments: +// ins -- The instruction being checked +// +// Return Value: +// true if the instruction qualifies; otherwise, false +// +bool emitter::IsJccInstruction(instruction ins) +{ + return ((ins >= INS_jo) && (ins <= INS_jg)) || ((ins >= INS_l_jo) && (ins <= INS_l_jg)); +} + +//------------------------------------------------------------------------ +// IsJmpInstruction: Determine if an instruction is a jump instruction but NOT a conditional jump instruction. +// +// Arguments: +// ins -- The instruction being checked +// +// Return Value: +// true if the instruction qualifies; otherwise, false +// +bool emitter::IsJmpInstruction(instruction ins) +{ + return +#ifdef TARGET_AMD64 + (ins == INS_rex_jmp) || +#endif + (ins == INS_i_jmp) || (ins == INS_jmp) || (ins == INS_l_jmp); +} + //---------------------------------------------------------------------------------------- // IsRedundantMov: // Check if the current `mov` instruction is redundant and can be omitted. @@ -8459,7 +8491,7 @@ void emitter::emitDispAddrMode(instrDesc* id, bool noDetail) lab = (insGroup*)emitCodeGetCookie(*bbp++); assert(lab); - printf("\n D" SIZE_LETTER " G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum); + printf("\n D" SIZE_LETTER " %s", emitLabelString(lab)); } while (--cnt); } } @@ -8691,22 +8723,16 @@ void emitter::emitDispIns( /* Display the instruction name */ - sstr = codeGen->genInsName(ins); + sstr = codeGen->genInsDisplayName(id); + printf(" %-9s", sstr); - if (IsAVXInstruction(ins) && !IsBMIInstruction(ins)) - { - printf(" v%-8s", sstr); - } - else - { - printf(" %-9s", sstr); - } #ifndef HOST_UNIX - if (strnlen_s(sstr, 10) >= 8) + if (strnlen_s(sstr, 10) >= 9) #else // HOST_UNIX - if (strnlen(sstr, 10) >= 8) + if (strnlen(sstr, 10) >= 9) #endif // HOST_UNIX { + // Make sure there's at least one space after the instruction name, for very long instruction names. printf(" "); } @@ -9656,7 +9682,7 @@ void emitter::emitDispIns( } else { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } } else @@ -14676,6 +14702,17 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins switch (ins) { case INS_align: +#if FEATURE_LOOP_ALIGN + if (id->idCodeSize() == 0) + { + // We're not going to generate any instruction, so it doesn't count for PerfScore. + result.insThroughput = PERFSCORE_THROUGHPUT_ZERO; + result.insLatency = PERFSCORE_LATENCY_ZERO; + break; + } +#endif + FALLTHROUGH; + case INS_nop: case INS_int3: assert(memFmt == IF_NONE); diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 8260445686be0..f952a6f649f44 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -83,7 +83,15 @@ code_t insEncodeOpreg(instruction ins, regNumber reg, emitAttr size); unsigned insSSval(unsigned scale); -bool IsAVXInstruction(instruction ins); +static bool IsSSEInstruction(instruction ins); +static bool IsSSEOrAVXInstruction(instruction ins); +static bool IsAVXOnlyInstruction(instruction ins); +static bool IsFMAInstruction(instruction ins); +static bool IsAVXVNNIInstruction(instruction ins); +static bool IsBMIInstruction(instruction ins); +static regNumber getBmiRegNumber(instruction ins); +static regNumber getSseShiftRegNumber(instruction ins); +bool IsAVXInstruction(instruction ins) const; code_t insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, code_t code); code_t AddRexWPrefix(instruction ins, code_t code); @@ -98,6 +106,9 @@ static bool IsMovInstruction(instruction ins); bool IsRedundantMov( instruction ins, insFormat fmt, emitAttr size, regNumber dst, regNumber src, bool canIgnoreSideEffects); +static bool IsJccInstruction(instruction ins); +static bool IsJmpInstruction(instruction ins); + bool AreUpper32BitsZero(regNumber reg); bool AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps treeOps); @@ -116,7 +127,8 @@ bool hasRexPrefix(code_t code) #define VEX_PREFIX_MASK_3BYTE 0xFF000000000000ULL #define VEX_PREFIX_CODE_3BYTE 0xC4000000000000ULL -bool TakesVexPrefix(instruction ins); +bool TakesVexPrefix(instruction ins) const; +static bool TakesRexWPrefix(instruction ins, emitAttr attr); // Returns true if the instruction encoding already contains VEX prefix bool hasVexPrefix(code_t code) @@ -142,7 +154,7 @@ code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr } bool useVEXEncodings; -bool UseVEXEncoding() +bool UseVEXEncoding() const { return useVEXEncodings; } diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 3b1b1739919d5..92a82c346a545 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1620,6 +1620,9 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) } } bNext->bbPreds = nullptr; + + // `block` can no longer be a loop pre-header (if it was before). + block->bbFlags &= ~BBF_LOOP_PREHEADER; } else { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 9422225c5d3de..277cd53e1c10b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -10294,7 +10294,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ { if (IS_CSE_INDEX(tree->gtCSEnum)) { - printf("CSE #%02d (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def")); + printf(FMT_CSE " (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def")); } else { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c12e46f02bdab..4aeeeb9e48633 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -494,6 +494,7 @@ enum GenTreeFlags : unsigned int GTF_INX_RNGCHK = 0x80000000, // GT_INDEX/GT_INDEX_ADDR -- the array reference should be range-checked. GTF_INX_STRING_LAYOUT = 0x40000000, // GT_INDEX -- this uses the special string array layout + GTF_INX_NOFAULT = 0x20000000, // GT_INDEX -- the INDEX does not throw an exception (morph to GTF_IND_NONFAULTING) GTF_IND_TGT_NOT_HEAP = 0x80000000, // GT_IND -- the target is not on the heap GTF_IND_VOLATILE = 0x40000000, // GT_IND -- the load or store must use volatile sematics (this is a nop on X86) diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 9ce10458fb08e..752626f20184d 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -24,11 +24,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ #ifdef DEBUG -/***************************************************************************** - * - * Returns the string representation of the given CPU instruction. - */ - +//----------------------------------------------------------------------------- +// genInsName: Returns the string representation of the given CPU instruction, as +// it exists in the instruction table. Note that some architectures don't encode the +// name completely in the table: xarch sometimes prepends a "v", and arm sometimes +// appends a "s". Use `genInsDisplayName()` to get a fully-formed name. +// const char* CodeGen::genInsName(instruction ins) { // clang-format off @@ -77,36 +78,36 @@ const char* CodeGen::genInsName(instruction ins) return insNames[ins]; } -void __cdecl CodeGen::instDisp(instruction ins, bool noNL, const char* fmt, ...) +//----------------------------------------------------------------------------- +// genInsDisplayName: Get a fully-formed instruction display name. This only handles +// the xarch case of prepending a "v", not the arm case of appending an "s". +// This can be called up to four times in a single 'printf' before the static buffers +// get reused. +// +// Returns: +// String with instruction name +// +const char* CodeGen::genInsDisplayName(emitter::instrDesc* id) { - if (compiler->opts.dspCode) - { - /* Display the instruction offset within the emit block */ - - // printf("[%08X:%04X]", GetEmitter().emitCodeCurBlock(), GetEmitter().emitCodeOffsInBlock()); - - /* Display the FP stack depth (before the instruction is executed) */ - - // printf("[FP=%02u] ", genGetFPstkLevel()); + instruction ins = id->idIns(); + const char* insName = genInsName(ins); - /* Display the instruction mnemonic */ - printf(" "); - - printf(" %-8s", genInsName(ins)); - - if (fmt) - { - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - } +#ifdef TARGET_XARCH + const int TEMP_BUFFER_LEN = 40; + static unsigned curBuf = 0; + static char buf[4][TEMP_BUFFER_LEN]; + const char* retbuf; - if (!noNL) - { - printf("\n"); - } + if (GetEmitter()->IsAVXInstruction(ins) && !GetEmitter()->IsBMIInstruction(ins)) + { + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "v%s", insName); + retbuf = buf[curBuf]; + curBuf = (curBuf + 1) % 4; + return retbuf; } +#endif // TARGET_XARCH + + return insName; } /*****************************************************************************/ diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 7254e205fbc48..b14597a870298 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -68,6 +68,9 @@ CONFIG_INTEGER(JitAlignLoopAdaptive, W("JitAlignLoopAdaptive"), 1) // If set, perform adaptive loop alignment that limits number of padding based on loop size. +// Print the alignment boundaries in disassembly. +CONFIG_INTEGER(JitDasmWithAlignmentBoundaries, W("JitDasmWithAlignmentBoundaries"), 0) + CONFIG_INTEGER(JitDirectAlloc, W("JitDirectAlloc"), 0) CONFIG_INTEGER(JitDoubleAlign, W("JitDoubleAlign"), 1) CONFIG_INTEGER(JitDumpASCII, W("JitDumpASCII"), 1) // Uses only ASCII characters in tree dumps diff --git a/src/coreclr/jit/jitstd/algorithm.h b/src/coreclr/jit/jitstd/algorithm.h index 000639a5a1de8..9fa6fbb94dd54 100644 --- a/src/coreclr/jit/jitstd/algorithm.h +++ b/src/coreclr/jit/jitstd/algorithm.h @@ -102,9 +102,9 @@ void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Less less // // It's not possible for newFirst to go past the end of the sort range: // - If newFirst reaches the pivot before newLast then the pivot is - // swapped to the right and we'll stop again when we reach it. + // swapped to the right and we'll stop again when we reach it. // - If newLast reaches the pivot before newFirst then the pivot is - // swapped to the left and the value at newFirst will take its place + // swapped to the left and the value at newFirst will take its place // to the right so less(newFirst, pivot) will again be false when the // old pivot's position is reached. do diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 0fe22420f783e..c3991e663e1d9 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -12,6 +12,40 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jitpch.h" +#ifdef DEBUG + +//-------------------------------------------------------------------------------------------------- +// ArrIndex::Print - debug print an ArrIndex struct in form: `V01[V02][V03]`. +// +// Arguments: +// dim (Optional) Print up to but not including this dimension. Default: print all dimensions. +// +void ArrIndex::Print(unsigned dim /* = -1 */) +{ + printf("V%02d", arrLcl); + for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) + { + printf("[V%02d]", indLcls.Get(i)); + } +} + +//-------------------------------------------------------------------------------------------------- +// ArrIndex::PrintBoundsCheckNodes - debug print an ArrIndex struct bounds check node tree ids in +// form: `[000125][000113]`. +// +// Arguments: +// dim (Optional) Print up to but not including this dimension. Default: print all dimensions. +// +void ArrIndex::PrintBoundsCheckNodes(unsigned dim /* = -1 */) +{ + for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) + { + Compiler::printTreeID(bndsChks.Get(i)); + } +} + +#endif // DEBUG + //-------------------------------------------------------------------------------------------------- // ToGenTree - Convert an arrLen operation into a gentree node. // @@ -39,11 +73,26 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb) { arr = comp->gtNewIndexRef(TYP_REF, arr, comp->gtNewLclvNode(arrIndex->indLcls[i], comp->lvaTable[arrIndex->indLcls[i]].lvType)); + + // Clear the range check flag and mark the index as non-faulting: we guarantee that all necessary range + // checking has already been done by the time this array index expression is invoked. + arr->gtFlags &= ~(GTF_INX_RNGCHK | GTF_EXCEPT); + arr->gtFlags |= GTF_INX_NOFAULT; } // If asked for arrlen invoke arr length operator. if (oper == ArrLen) { GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb); + + // We already guaranteed (by a sequence of preceding checks) that the array length operator will not + // throw an exception because we null checked the base array. + // So, we should be able to do the following: + // arrLen->gtFlags &= ~GTF_EXCEPT; + // arrLen->gtFlags |= GTF_IND_NONFAULTING; + // However, we then end up with a mix of non-faulting array length operators as well as normal faulting + // array length operators in the slow-path of the cloned loops. CSE doesn't keep these separate, so bails + // out on creating CSEs on this very useful type of CSE, leading to CQ losses in the cloned loop fast path. + // TODO-CQ: fix this. return arrLen; } else @@ -395,26 +444,30 @@ void LoopCloneContext::PrintBlockConditions(unsigned loopNum) { printf("Block conditions:\n"); - JitExpandArrayStack*>* levelCond = blockConditions[loopNum]; - if (levelCond == nullptr || levelCond->Size() == 0) + JitExpandArrayStack*>* blockConds = blockConditions[loopNum]; + if (blockConds == nullptr || blockConds->Size() == 0) { printf("No block conditions\n"); return; } - for (unsigned i = 0; i < levelCond->Size(); ++i) + for (unsigned i = 0; i < blockConds->Size(); ++i) + { + PrintBlockLevelConditions(i, (*blockConds)[i]); + } +} +void LoopCloneContext::PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond) +{ + printf("%d = ", level); + for (unsigned j = 0; j < levelCond->Size(); ++j) { - printf("%d = {", i); - for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j) + if (j != 0) { - if (j != 0) - { - printf(" & "); - } - (*((*levelCond)[i]))[j].Print(); + printf(" & "); } - printf("}\n"); + (*levelCond)[j].Print(); } + printf("\n"); } #endif @@ -650,19 +703,19 @@ void LoopCloneContext::PrintConditions(unsigned loopNum) { if (conditions[loopNum] == nullptr) { - JITDUMP("NO conditions"); + printf("NO conditions"); return; } if (conditions[loopNum]->Size() == 0) { - JITDUMP("Conditions were optimized away! Will always take cloned path."); + printf("Conditions were optimized away! Will always take cloned path."); return; } for (unsigned i = 0; i < conditions[loopNum]->Size(); ++i) { if (i != 0) { - JITDUMP(" & "); + printf(" & "); } (*conditions[loopNum])[i].Print(); } @@ -711,6 +764,9 @@ void LoopCloneContext::CondToStmtInBlock(Compiler* comp comp->fgInsertStmtAtEnd(block, stmt); // Remorph. + JITDUMP("Loop cloning condition tree before morphing:\n"); + DBEXEC(comp->verbose, comp->gtDispTree(jmpTrueTree)); + JITDUMP("\n"); comp->fgMorphBlockStmt(block, stmt DEBUGARG("Loop cloning condition")); } @@ -965,16 +1021,15 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext for (unsigned i = 0; i < optInfos->Size(); ++i) { - LcOptInfo* optInfo = optInfos->GetRef(i); + LcOptInfo* optInfo = optInfos->Get(i); switch (optInfo->GetOptType()) { case LcOptInfo::LcJaggedArray: { // limit <= arrLen LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); - LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen); - LC_Ident arrLenIdent = LC_Ident(arrLen); - + LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen); + LC_Ident arrLenIdent = LC_Ident(arrLen); LC_Condition cond(GT_LE, LC_Expr(ident), LC_Expr(arrLenIdent)); context->EnsureConditions(loopNum)->Push(cond); @@ -1000,9 +1055,9 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext return false; } } - JITDUMP("Conditions: ("); + JITDUMP("Conditions: "); DBEXEC(verbose, context->PrintConditions(loopNum)); - JITDUMP(")\n"); + JITDUMP("\n"); return true; } return false; @@ -1164,10 +1219,6 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con printf("Deref condition tree:\n"); for (unsigned i = 0; i < nodes.Size(); ++i) { - if (i != 0) - { - printf(","); - } nodes[i]->Print(); printf("\n"); } @@ -1176,6 +1227,7 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con if (maxRank == -1) { + JITDUMP("> maxRank undefined\n"); return false; } @@ -1184,12 +1236,13 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con // So add 1 after rank * 2. unsigned condBlocks = (unsigned)maxRank * 2 + 1; - // Heuristic to not create too many blocks. - // REVIEW: due to the definition of `condBlocks`, above, the effective max is 3 blocks, meaning - // `maxRank` of 1. Question: should the heuristic allow more blocks to be created in some situations? - // REVIEW: make this based on a COMPlus configuration? - if (condBlocks > 4) + // Heuristic to not create too many blocks. Defining as 3 allows, effectively, loop cloning on + // doubly-nested loops. + // REVIEW: make this based on a COMPlus configuration, at least for debug? + const unsigned maxAllowedCondBlocks = 3; + if (condBlocks > maxAllowedCondBlocks) { + JITDUMP("> Too many condition blocks (%u > %u)\n", condBlocks, maxAllowedCondBlocks); return false; } @@ -1254,14 +1307,60 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum); for (unsigned i = 0; i < optInfos->Size(); ++i) { - LcOptInfo* optInfo = optInfos->GetRef(i); + LcOptInfo* optInfo = optInfos->Get(i); switch (optInfo->GetOptType()) { case LcOptInfo::LcJaggedArray: { LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); compCurBB = arrIndexInfo->arrIndex.useBlock; - optRemoveCommaBasedRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt); + + // Remove all bounds checks for this array up to (and including) `arrIndexInfo->dim`. So, if that is 1, + // Remove rank 0 and 1 bounds checks. + + for (unsigned dim = 0; dim <= arrIndexInfo->dim; dim++) + { + GenTree* bndsChkNode = arrIndexInfo->arrIndex.bndsChks[dim]; + +#ifdef DEBUG + if (verbose) + { + printf("Remove bounds check "); + printTreeID(bndsChkNode->gtGetOp1()); + printf(" for " FMT_STMT ", dim% d, ", arrIndexInfo->stmt->GetID(), dim); + arrIndexInfo->arrIndex.Print(); + printf(", bounds check nodes: "); + arrIndexInfo->arrIndex.PrintBoundsCheckNodes(); + printf("\n"); + } +#endif // DEBUG + + if (bndsChkNode->gtGetOp1()->OperIsBoundsCheck()) + { + // This COMMA node will only represent a bounds check if we've haven't already removed this + // bounds check in some other nesting cloned loop. For example, consider: + // for (i = 0; i < x; i++) + // for (j = 0; j < y; j++) + // a[i][j] = i + j; + // If the outer loop is cloned first, it will remove the a[i] bounds check from the optimized + // path. Later, when the inner loop is cloned, we want to remove the a[i][j] bounds check. If + // we clone the inner loop, we know that the a[i] bounds check isn't required because we'll add + // it to the loop cloning conditions. On the other hand, we can clone a loop where we get rid of + // the nested bounds check but nobody has gotten rid of the outer bounds check. As before, we + // know the outer bounds check is not needed because it's been added to the cloning conditions, + // so we can get rid of the bounds check here. + // + optRemoveCommaBasedRangeCheck(bndsChkNode, arrIndexInfo->stmt); + } + else + { + JITDUMP(" Bounds check already removed\n"); + + // If the bounds check node isn't there, it better have been converted to a GT_NOP. + assert(bndsChkNode->gtGetOp1()->OperIs(GT_NOP)); + } + } + DBEXEC(dynamicPath, optDebugLogLoopCloning(arrIndexInfo->arrIndex.useBlock, arrIndexInfo->stmt)); } break; @@ -1474,8 +1573,9 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, BasicBlock* head, BasicBlock* slowHead) { - JITDUMP("Inserting loop cloning conditions\n"); + JITDUMP("Inserting loop " FMT_LP " loop choice conditions\n", loopNum); assert(context->HasBlockConditions(loopNum)); + assert(head->bbJumpKind == BBJ_COND); BasicBlock* curCond = head; JitExpandArrayStack*>* levelCond = context->GetBlockConditions(loopNum); @@ -1484,10 +1584,15 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, bool isHeaderBlock = (curCond == head); // Flip the condition if header block. + JITDUMP("Adding loop " FMT_LP " level %u block conditions to " FMT_BB "\n ", loopNum, i, curCond->bbNum); + DBEXEC(verbose, context->PrintBlockLevelConditions(i, (*levelCond)[i])); context->CondToStmtInBlock(this, *((*levelCond)[i]), curCond, /*reverse*/ isHeaderBlock); // Create each condition block ensuring wiring between them. - BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true); + BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true); + tmp->inheritWeight(head); + tmp->bbNatLoopNum = head->bbNatLoopNum; + curCond->bbJumpDest = isHeaderBlock ? tmp : slowHead; JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", curCond->bbNum, curCond->bbJumpDest->bbNum); @@ -1500,13 +1605,13 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, } curCond = tmp; - - curCond->inheritWeight(head); - curCond->bbNatLoopNum = head->bbNatLoopNum; - JITDUMP("Created new " FMT_BB " for new level %u\n", curCond->bbNum, i); } // Finally insert cloning conditions after all deref conditions have been inserted. + JITDUMP("Adding loop " FMT_LP " cloning conditions to " FMT_BB "\n", loopNum, curCond->bbNum); + JITDUMP(" "); + DBEXEC(verbose, context->PrintConditions(loopNum)); + JITDUMP("\n"); context->CondToStmtInBlock(this, *(context->GetConditions(loopNum)), curCond, /*reverse*/ false); return curCond; } @@ -2222,10 +2327,12 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop #ifdef DEBUG if (verbose) { - printf("Found ArrIndex at tree "); + printf("Found ArrIndex at " FMT_BB " " FMT_STMT " tree ", arrIndex.useBlock->bbNum, info->stmt->GetID()); printTreeID(tree); printf(" which is equivalent to: "); arrIndex.Print(); + printf(", bounds check nodes: "); + arrIndex.PrintBoundsCheckNodes(); printf("\n"); } #endif @@ -2233,6 +2340,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop // Check that the array object local variable is invariant within the loop body. if (!optIsStackLocalInvariant(info->loopNum, arrIndex.arrLcl)) { + JITDUMP("V%02d is not loop invariant\n", arrIndex.arrLcl); return WALK_SKIP_SUBTREES; } diff --git a/src/coreclr/jit/loopcloning.h b/src/coreclr/jit/loopcloning.h index 6cc921c520db1..c5ed53c31bad8 100644 --- a/src/coreclr/jit/loopcloning.h +++ b/src/coreclr/jit/loopcloning.h @@ -220,14 +220,8 @@ struct ArrIndex } #ifdef DEBUG - void Print(unsigned dim = -1) - { - printf("V%02d", arrLcl); - for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) - { - printf("[V%02d]", indLcls.GetRef(i)); - } - } + void Print(unsigned dim = -1); + void PrintBoundsCheckNodes(unsigned dim = -1); #endif }; @@ -260,9 +254,8 @@ struct LcOptInfo #include "loopcloningopts.h" }; - void* optInfo; OptType optType; - LcOptInfo(void* optInfo, OptType optType) : optInfo(optInfo), optType(optType) + LcOptInfo(OptType optType) : optType(optType) { } @@ -270,6 +263,7 @@ struct LcOptInfo { return optType; } + #define LC_OPT(en) \ en##OptInfo* As##en##OptInfo() \ { \ @@ -292,7 +286,7 @@ struct LcMdArrayOptInfo : public LcOptInfo ArrIndex* index; // "index" cached computation in the form of an ArrIndex representation. LcMdArrayOptInfo(GenTreeArrElem* arrElem, unsigned dim) - : LcOptInfo(this, LcMdArray), arrElem(arrElem), dim(dim), index(nullptr) + : LcOptInfo(LcMdArray), arrElem(arrElem), dim(dim), index(nullptr) { } @@ -325,7 +319,7 @@ struct LcJaggedArrayOptInfo : public LcOptInfo Statement* stmt; // "stmt" where the optimization opportunity occurs. LcJaggedArrayOptInfo(ArrIndex& arrIndex, unsigned dim, Statement* stmt) - : LcOptInfo(this, LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt) + : LcOptInfo(LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt) { } }; @@ -678,30 +672,24 @@ struct LoopCloneContext CompAllocator alloc; // The allocator // The array of optimization opportunities found in each loop. (loop x optimization-opportunities) - JitExpandArrayStack** optInfo; + jitstd::vector*> optInfo; // The array of conditions that influence which path to take for each loop. (loop x cloning-conditions) - JitExpandArrayStack** conditions; + jitstd::vector*> conditions; // The array of dereference conditions found in each loop. (loop x deref-conditions) - JitExpandArrayStack** derefs; + jitstd::vector*> derefs; // The array of block levels of conditions for each loop. (loop x level x conditions) - JitExpandArrayStack*>** blockConditions; + jitstd::vector*>*> blockConditions; - LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc) + LoopCloneContext(unsigned loopCount, CompAllocator alloc) + : alloc(alloc), optInfo(alloc), conditions(alloc), derefs(alloc), blockConditions(alloc) { - optInfo = new (alloc) JitExpandArrayStack*[loopCount]; - conditions = new (alloc) JitExpandArrayStack*[loopCount]; - derefs = new (alloc) JitExpandArrayStack*[loopCount]; - blockConditions = new (alloc) JitExpandArrayStack*>*[loopCount]; - for (unsigned i = 0; i < loopCount; ++i) - { - optInfo[i] = nullptr; - conditions[i] = nullptr; - derefs[i] = nullptr; - blockConditions[i] = nullptr; - } + optInfo.resize(loopCount, nullptr); + conditions.resize(loopCount, nullptr); + derefs.resize(loopCount, nullptr); + blockConditions.resize(loopCount, nullptr); } // Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true. @@ -739,6 +727,7 @@ struct LoopCloneContext #ifdef DEBUG // Print the block conditions for the loop. void PrintBlockConditions(unsigned loopNum); + void PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond); #endif // Does the loop have block conditions? diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 526a996e1b40c..68f31af763634 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5581,8 +5581,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) GenTree* arrRef = asIndex->Arr(); GenTree* index = asIndex->Index(); - bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled - bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0); + bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled + bool indexNonFaulting = ((tree->gtFlags & GTF_INX_NOFAULT) != 0); // if true, mark GTF_IND_NONFAULTING + bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0); GenTree* arrRefDefn = nullptr; // non-NULL if we need to allocate a temp for the arrRef expression GenTree* indexDefn = nullptr; // non-NULL if we need to allocate a temp for the index expression @@ -5742,9 +5743,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) this->compFloatingPointUsed = true; } - // We've now consumed the GTF_INX_RNGCHK, and the node + // We've now consumed the GTF_INX_RNGCHK and GTF_INX_NOFAULT, and the node // is no longer a GT_INDEX node. - tree->gtFlags &= ~GTF_INX_RNGCHK; + tree->gtFlags &= ~(GTF_INX_RNGCHK | GTF_INX_NOFAULT); tree->AsOp()->gtOp1 = addr; @@ -5752,7 +5753,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) tree->gtFlags |= GTF_IND_ARR_INDEX; // If there's a bounds check, the indir won't fault. - if (bndsChk) + if (bndsChk || indexNonFaulting) { tree->gtFlags |= GTF_IND_NONFAULTING; } diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index b4137ec52ebbf..587349e489b55 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -166,7 +166,8 @@ Compiler::fgWalkResult Compiler::optCSE_MaskHelper(GenTree** pTree, fgWalkData* if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned cseIndex = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseBit = genCSEnum2bit(cseIndex); + // Note that we DO NOT use getCSEAvailBit() here, for the CSE_defMask/CSE_useMask + unsigned cseBit = genCSEnum2bit(cseIndex); if (IS_CSE_DEF(tree->gtCSEnum)) { BitVecOps::AddElemD(comp->cseMaskTraits, pUserData->CSE_defMask, cseBit); @@ -424,16 +425,16 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) ValueNum vnLibNorm = vnStore->VNNormalValue(vnLib); // We use the normal value number because we want the CSE candidate to - // represent all expressions that produce the same normal value number + // represent all expressions that produce the same normal value number. // We will handle the case where we have different exception sets when // promoting the candidates. // // We do this because a GT_IND will usually have a NullPtrExc entry in its // exc set, but we may have cleared the GTF_EXCEPT flag and if so, it won't - // have an NullPtrExc, or we may have assigned the value of an GT_IND + // have an NullPtrExc, or we may have assigned the value of an GT_IND // into a LCL_VAR and then read it back later. // - // When we are promoting the CSE candidates we insure that any CSE + // When we are promoting the CSE candidates we ensure that any CSE // uses that we promote have an exc set that is the same as the CSE defs // or have an empty set. And that all of the CSE defs produced the required // set of exceptions for the CSE uses. @@ -502,7 +503,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) key = vnLibNorm; } - // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst + // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst. // Note that when isSharedConst is true then we require that the TARGET_SIGN_BIT is set in the key // and otherwise we require that we never create a ValueNumber with the TARGET_SIGN_BIT set. // @@ -709,7 +710,6 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) C_ASSERT((signed char)MAX_CSE_CNT == MAX_CSE_CNT); unsigned CSEindex = ++optCSECandidateCount; - // EXPSET_TP CSEmask = genCSEnum2bit(CSEindex); /* Record the new CSE index in the hashDsc */ hashDsc->csdIndex = CSEindex; @@ -746,16 +746,14 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) } } -/***************************************************************************** - * - * Locate CSE candidates and assign indices to them - * return 0 if no CSE candidates were found - */ - -unsigned Compiler::optValnumCSE_Locate() +//------------------------------------------------------------------------ +// optValnumCSE_Locate: Locate CSE candidates and assign them indices. +// +// Returns: +// true if there are any CSE candidates, false otherwise +// +bool Compiler::optValnumCSE_Locate() { - // Locate CSE candidates and assign them indices - bool enableConstCSE = true; int configValue = JitConfig.JitConstCSE(); @@ -871,14 +869,14 @@ unsigned Compiler::optValnumCSE_Locate() if (!optDoCSE) { - return 0; + return false; } /* We're finished building the expression lookup table */ optCSEstop(); - return 1; + return true; } //------------------------------------------------------------------------ @@ -890,7 +888,7 @@ unsigned Compiler::optValnumCSE_Locate() // // Arguments: // compare - The compare node to check - +// void Compiler::optCseUpdateCheckedBoundMap(GenTree* compare) { assert(compare->OperIsCompare()); @@ -999,9 +997,9 @@ void Compiler::optValnumCSE_InitDataFlow() // Init traits and cseCallKillsMask bitvectors. cseLivenessTraits = new (getAllocator(CMK_CSE)) BitVecTraits(bitCount, this); cseCallKillsMask = BitVecOps::MakeEmpty(cseLivenessTraits); - for (unsigned inx = 0; inx < optCSECandidateCount; inx++) + for (unsigned inx = 1; inx <= optCSECandidateCount; inx++) { - unsigned cseAvailBit = inx * 2; + unsigned cseAvailBit = getCSEAvailBit(inx); // a one preserves availability and a zero kills the availability // we generate this kind of bit pattern: 101010101010 @@ -1047,7 +1045,7 @@ void Compiler::optValnumCSE_InitDataFlow() block->bbCseGen = BitVecOps::MakeEmpty(cseLivenessTraits); } - // We walk the set of CSE candidates and set the bit corresponsing to the CSEindex + // We walk the set of CSE candidates and set the bit corresponding to the CSEindex // in the block's bbCseGen bitset // for (unsigned inx = 0; inx < optCSECandidateCount; inx++) @@ -1060,16 +1058,16 @@ void Compiler::optValnumCSE_InitDataFlow() while (lst != nullptr) { BasicBlock* block = lst->tslBlock; - unsigned CseAvailBit = genCSEnum2bit(CSEindex) * 2; - unsigned cseAvailCrossCallBit = CseAvailBit + 1; + unsigned cseAvailBit = getCSEAvailBit(CSEindex); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEindex); - // This CSE is generated in 'block', we always set the CseAvailBit + // This CSE is generated in 'block', we always set the cseAvailBit // If this block does not contain a call, we also set cseAvailCrossCallBit // // If we have a call in this block then in the loop below we walk the trees // backwards to find any CSEs that are generated after the last call in the block. // - BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, CseAvailBit); + BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailBit); if ((block->bbFlags & BBF_HAS_CALL) == 0) { BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit); @@ -1113,7 +1111,7 @@ void Compiler::optValnumCSE_InitDataFlow() if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1; + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit); } if (tree->OperGet() == GT_CALL) @@ -1142,15 +1140,16 @@ void Compiler::optValnumCSE_InitDataFlow() bool headerPrinted = false; for (BasicBlock* const block : Blocks()) { - if (block->bbCseGen != nullptr) + if (!BitVecOps::IsEmpty(cseLivenessTraits, block->bbCseGen)) { if (!headerPrinted) { printf("\nBlocks that generate CSE def/uses\n"); headerPrinted = true; } - printf(FMT_BB, block->bbNum); - printf(" cseGen = %s\n", genES2str(cseLivenessTraits, block->bbCseGen)); + printf(FMT_BB " cseGen = ", block->bbNum); + optPrintCSEDataFlowSet(block->bbCseGen); + printf("\n"); } } } @@ -1184,6 +1183,7 @@ class CSE_DataFlow // BitVecOps::Assign(m_comp->cseLivenessTraits, m_preMergeOut, block->bbCseOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1191,11 +1191,13 @@ class CSE_DataFlow printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } #endif // DEBUG +#endif // 0 } // Merge: perform the merging of each of the predecessor's liveness values (since this is a forward analysis) void Merge(BasicBlock* block, BasicBlock* predBlock, unsigned dupCount) { +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1204,15 +1206,18 @@ class CSE_DataFlow printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } #endif // DEBUG +#endif // 0 BitVecOps::IntersectionD(m_comp->cseLivenessTraits, block->bbCseIn, predBlock->bbCseOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { printf(" => cseIn = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseIn)); } #endif // DEBUG +#endif // 0 } //------------------------------------------------------------------------ @@ -1272,6 +1277,7 @@ class CSE_DataFlow // bool notDone = !BitVecOps::Equal(m_comp->cseLivenessTraits, block->bbCseOut, m_preMergeOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1288,6 +1294,7 @@ class CSE_DataFlow notDone ? "true" : "false"); } #endif // DEBUG +#endif // 0 return notDone; } @@ -1330,10 +1337,12 @@ void Compiler::optValnumCSE_DataFlow() for (BasicBlock* const block : Blocks()) { - printf(FMT_BB, block->bbNum); - printf(" cseIn = %s,", genES2str(cseLivenessTraits, block->bbCseIn)); - printf(" cseGen = %s,", genES2str(cseLivenessTraits, block->bbCseGen)); - printf(" cseOut = %s", genES2str(cseLivenessTraits, block->bbCseOut)); + printf(FMT_BB " in gen out\n", block->bbNum); + optPrintCSEDataFlowSet(block->bbCseIn); + printf("\n"); + optPrintCSEDataFlowSet(block->bbCseGen); + printf("\n"); + optPrintCSEDataFlowSet(block->bbCseOut); printf("\n"); } @@ -1347,17 +1356,17 @@ void Compiler::optValnumCSE_DataFlow() // // Using the information computed by CSE_DataFlow determine for each // CSE whether the CSE is a definition (if the CSE was not available) -// or if the CSE is a use (if the CSE was previously made available) -// The implementation iterates of all blocks setting 'available_cses' +// or if the CSE is a use (if the CSE was previously made available). +// The implementation iterates over all blocks setting 'available_cses' // to the CSEs that are available at input to the block. // When a CSE expression is encountered it is classified as either // as a definition (if the CSE is not in the 'available_cses' set) or -// as a use (if the CSE is in the 'available_cses' set). If the CSE +// as a use (if the CSE is in the 'available_cses' set). If the CSE // is a definition then it is added to the 'available_cses' set. // // This algorithm uncovers the defs and uses gradually and as it does // so it also builds the exception set that all defs make: 'defExcSetCurrent' -// and the exception set that the uses we have seen depend upon: 'defExcSetPromise' +// and the exception set that the uses we have seen depend upon: 'defExcSetPromise'. // // Typically expressions with the same normal ValueNum generate exactly the // same exception sets. There are two way that we can get different exception @@ -1371,12 +1380,11 @@ void Compiler::optValnumCSE_DataFlow() // 2. We stored an expression into a LclVar or into Memory and read it later // e.g. t = p.a; // e1 = (t + q.b) :: e1 has one NullPtrExc and e2 has two. -// e2 = (p.a + q.b) but both compute the same normal value// +// e2 = (p.a + q.b) but both compute the same normal value // e.g. m.a = p.a; // e1 = (m.a + q.b) :: e1 and e2 have different exception sets. // e2 = (p.a + q.b) but both compute the same normal value // -// void Compiler::optValnumCSE_Availablity() { #ifdef DEBUG @@ -1411,12 +1419,12 @@ void Compiler::optValnumCSE_Availablity() if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned CseAvailBit = genCSEnum2bit(CSEnum) * 2; - unsigned cseAvailCrossCallBit = CseAvailBit + 1; + unsigned cseAvailBit = getCSEAvailBit(CSEnum); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); CSEdsc* desc = optCSEfindDsc(CSEnum); BasicBlock::weight_t stmw = block->getBBWeight(this); - isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, CseAvailBit); + isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, cseAvailBit); isDef = !isUse; // If is isn't a CSE use, it is a CSE def // Is this a "use", that we haven't yet marked as live across a call @@ -1446,7 +1454,7 @@ void Compiler::optValnumCSE_Availablity() printf(FMT_BB " ", block->bbNum); printTreeID(tree); - printf(" %s of CSE #%02u [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw), + printf(" %s of " FMT_CSE " [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw), madeLiveAcrossCall ? " *** Now Live Across Call ***" : ""); } #endif // DEBUG @@ -1477,7 +1485,7 @@ void Compiler::optValnumCSE_Availablity() // Is defExcSetCurrent still set to the uninit marker value of VNForNull() ? if (desc->defExcSetCurrent == vnStore->VNForNull()) { - // This is the first time visited, so record this defs exeception set + // This is the first time visited, so record this defs exception set desc->defExcSetCurrent = theLiberalExcSet; } @@ -1589,7 +1597,7 @@ void Compiler::optValnumCSE_Availablity() tree->gtCSEnum = TO_CSE_DEF(tree->gtCSEnum); // This CSE becomes available after this def - BitVecOps::AddElemD(cseLivenessTraits, available_cses, CseAvailBit); + BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailBit); BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit); } else // We are visiting a CSE use @@ -1636,7 +1644,7 @@ void Compiler::optValnumCSE_Availablity() if (!vnStore->VNExcIsSubset(desc->defExcSetPromise, theLiberalExcSet)) { // We can't safely make this into a CSE use, because this - // CSE use has an exeception set item that is not promised + // CSE use has an exception set item that is not promised // by all of our CSE defs. // // We will omit this CSE use from the graph and proceed, @@ -1660,7 +1668,7 @@ void Compiler::optValnumCSE_Availablity() // In order to determine if a CSE is live across a call, we model availablity using two bits and // kill all of the cseAvailCrossCallBit for each CSE whenever we see a GT_CALL (unless the call - // generates A cse) + // generates a CSE). // if (tree->OperGet() == GT_CALL) { @@ -1690,7 +1698,7 @@ void Compiler::optValnumCSE_Availablity() // available_cses // unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1; + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit); } @@ -2027,14 +2035,14 @@ class CSE_Heuristic if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey)) { - printf("CSE #%02u, {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", + printf(FMT_CSE ", {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, dsc->csdHashKey, dsc->defExcSetPromise, dsc->csdUseCount, def, use, cost, dsc->csdLiveAcrossCall ? ", call" : " "); } else { size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey); - printf("CSE #%02u, {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, + printf(FMT_CSE ", {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, dspPtr(kVal), dsc->csdUseCount, def, use, cost, dsc->csdLiveAcrossCall ? ", call" : " "); } @@ -2814,7 +2822,7 @@ class CSE_Heuristic if (dsc->csdDefCount == 1) { - JITDUMP("CSE #%02u is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex, + JITDUMP(FMT_CSE " is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex, cseLclVarNum); m_pCompiler->lvaTable[cseLclVarNum].lvInSsa = true; @@ -2931,7 +2939,7 @@ class CSE_Heuristic if (IS_CSE_INDEX(lst->tslTree->gtCSEnum)) { ValueNum currVN = m_pCompiler->vnStore->VNLiberalNormalValue(lst->tslTree->gtVNPair); - printf("0x%x(%s " FMT_VN ") ", lst->tslTree, + printf("[%06d](%s " FMT_VN ") ", m_pCompiler->dspTreeID(lst->tslTree), IS_CSE_USE(lst->tslTree->gtCSEnum) ? "use" : "def", currVN); } lst = lst->tslNext; @@ -2996,7 +3004,7 @@ class CSE_Heuristic #ifdef DEBUG if (m_pCompiler->verbose) { - printf("\nWorking on the replacement of the CSE #%02u use at ", exp->gtCSEnum); + printf("\nWorking on the replacement of the " FMT_CSE " use at ", exp->gtCSEnum); Compiler::printTreeID(exp); printf(" in " FMT_BB "\n", blk->bbNum); } @@ -3164,7 +3172,7 @@ class CSE_Heuristic #ifdef DEBUG if (m_pCompiler->verbose) { - printf("\nCSE #%02u def at ", GET_CSE_INDEX(exp->gtCSEnum)); + printf("\n" FMT_CSE " def at ", GET_CSE_INDEX(exp->gtCSEnum)); Compiler::printTreeID(exp); printf(" replaced in " FMT_BB " with def of V%02u\n", blk->bbNum, cseLclVarNum); } @@ -3314,13 +3322,13 @@ class CSE_Heuristic if (dsc->defExcSetPromise == ValueNumStore::NoVN) { - JITDUMP("Abandoned CSE #%02u because we had defs with different Exc sets\n", candidate.CseIndex()); + JITDUMP("Abandoned " FMT_CSE " because we had defs with different Exc sets\n", candidate.CseIndex()); continue; } if (dsc->csdStructHndMismatch) { - JITDUMP("Abandoned CSE #%02u because we had mismatching struct handles\n", candidate.CseIndex()); + JITDUMP("Abandoned " FMT_CSE " because we had mismatching struct handles\n", candidate.CseIndex()); continue; } @@ -3328,7 +3336,7 @@ class CSE_Heuristic if (candidate.UseCount() == 0) { - JITDUMP("Skipped CSE #%02u because use count is 0\n", candidate.CseIndex()); + JITDUMP("Skipped " FMT_CSE " because use count is 0\n", candidate.CseIndex()); continue; } @@ -3337,14 +3345,14 @@ class CSE_Heuristic { if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey)) { - printf("\nConsidering CSE #%02u {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n", + printf("\nConsidering " FMT_CSE " {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), dsc->csdHashKey, dsc->defExcSetPromise, candidate.DefCount(), candidate.UseCount(), candidate.Cost(), dsc->csdLiveAcrossCall ? ", call" : " "); } else { size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey); - printf("\nConsidering CSE #%02u {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), + printf("\nConsidering " FMT_CSE " {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), dspPtr(kVal), candidate.DefCount(), candidate.UseCount(), candidate.Cost(), dsc->csdLiveAcrossCall ? ", call" : " "); } @@ -3428,11 +3436,6 @@ void Compiler::optValnumCSE_Heuristic() void Compiler::optOptimizeValnumCSEs() { #ifdef DEBUG - if (verbose) - { - printf("\n*************** In optOptimizeValnumCSEs()\n"); - } - if (optConfigDisableCSE()) { return; // Disabled by JitNoCSE @@ -3441,22 +3444,13 @@ void Compiler::optOptimizeValnumCSEs() optValnumCSE_phase = true; - /* Initialize the expression tracking logic */ - optValnumCSE_Init(); - /* Locate interesting expressions and assign indices to them */ - - if (optValnumCSE_Locate() > 0) + if (optValnumCSE_Locate()) { - optCSECandidateTotal += optCSECandidateCount; - optValnumCSE_InitDataFlow(); - optValnumCSE_DataFlow(); - optValnumCSE_Availablity(); - optValnumCSE_Heuristic(); } @@ -3807,21 +3801,11 @@ bool Compiler::optConfigDisableCSE2() void Compiler::optOptimizeCSEs() { -#ifdef DEBUG - if (verbose) - { - printf("\n*************** In optOptimizeCSEs()\n"); - printf("Blocks/Trees at start of optOptimizeCSE phase\n"); - fgDispBasicBlocks(true); - } -#endif // DEBUG - optCSECandidateCount = 0; optCSEstart = lvaCount; INDEBUG(optEnsureClearCSEInfo()); optOptimizeValnumCSEs(); - EndPhase(PHASE_OPTIMIZE_VALNUM_CSES); } /***************************************************************************** @@ -3873,4 +3857,38 @@ void Compiler::optEnsureClearCSEInfo() } } +//------------------------------------------------------------------------ +// optPrintCSEDataFlowSet: Print out one of the CSE dataflow sets bbCseGen, bbCseIn, bbCseOut, +// interpreting the bits in a more useful way for the dump. +// +// Arguments: +// cseDataFlowSet - One of the dataflow sets to display +// includeBits - Display the actual bits of the set as well +// +void Compiler::optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits /* = true */) +{ + if (includeBits) + { + printf("%s ", genES2str(cseLivenessTraits, cseDataFlowSet)); + } + + bool first = true; + for (unsigned cseIndex = 1; cseIndex <= optCSECandidateCount; cseIndex++) + { + unsigned cseAvailBit = getCSEAvailBit(cseIndex); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(cseIndex); + + if (BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailBit)) + { + if (!first) + { + printf(", "); + } + const bool isAvailCrossCall = BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailCrossCallBit); + printf(FMT_CSE "%s", cseIndex, isAvailCrossCall ? ".c" : ""); + first = false; + } + } +} + #endif // DEBUG diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index a7df5b95905a2..748d5feabb562 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -38,7 +38,6 @@ void Compiler::optInit() optNativeCallCount = 0; optAssertionCount = 0; optAssertionDep = nullptr; - optCSECandidateTotal = 0; optCSEstart = UINT_MAX; optCSEcount = 0; } @@ -5627,8 +5626,8 @@ void Compiler::optHoistLoopCode() #endif #if 0 - // The code in this #if has been useful in debugging loop cloning issues, by - // enabling selective enablement of the loop cloning optimization according to + // The code in this #if has been useful in debugging loop hoisting issues, by + // enabling selective enablement of the loop hoisting optimization according to // method hash. #ifdef DEBUG unsigned methHash = info.compMethodHash(); @@ -5650,7 +5649,7 @@ void Compiler::optHoistLoopCode() return; printf("Doing loop hoisting in %s (0x%x).\n", info.compFullName, methHash); #endif // DEBUG -#endif // 0 -- debugging loop cloning issues +#endif // 0 -- debugging loop hoisting issues #ifdef DEBUG if (verbose) @@ -5899,6 +5898,8 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt) printf("\n LOOPV-FP(%d)=", pLoopDsc->lpLoopVarFPCount); lvaDispVarSet(loopFPVars); + + printf("\n"); } #endif } diff --git a/src/coreclr/jit/phase.cpp b/src/coreclr/jit/phase.cpp index 530f8e74cbba0..f08134bf11532 100644 --- a/src/coreclr/jit/phase.cpp +++ b/src/coreclr/jit/phase.cpp @@ -84,8 +84,9 @@ void Phase::PrePhase() // // Currently the list is just the set of phases that have custom // derivations from the Phase class. - static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_RATIONALIZE, PHASE_LOWERING, PHASE_STACK_LEVEL_SETTER}; - bool doPrePhase = false; + static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_OPTIMIZE_VALNUM_CSES, PHASE_RATIONALIZE, PHASE_LOWERING, + PHASE_STACK_LEVEL_SETTER}; + bool doPrePhase = false; for (size_t i = 0; i < sizeof(s_allowlist) / sizeof(Phases); i++) { From fc5df59819dd58303241ea0f1d602f43154e13ee Mon Sep 17 00:00:00 2001 From: Jeff Schwartz Date: Sat, 10 Jul 2021 10:29:01 -0700 Subject: [PATCH 61/72] add newly added label (#55462) --- docs/area-owners.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/area-owners.md b/docs/area-owners.md index 4188ea7d982a6..740404f771aa2 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -79,6 +79,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-System.Diagnostics-mono | @lewing | @thaystg @radical | | | area-System.Diagnostics.Activity | @tommcdon | @tarekgh | | | area-System.Diagnostics.EventLog | @ericstj | @Anipik @ViktorHofer | | +| area-System.Diagnostics.Metric | @tommcdon | @noahfalk | | | area-System.Diagnostics.PerformanceCounter | @ericstj | @Anipik @ViktorHofer | | | area-System.Diagnostics.Process | @jeffhandley | @adamsitnik @carlossanlop @jozkee | | | area-System.Diagnostics.Tracing | @tommcdon | @noahfalk @tommcdon @Anipik @ViktorHofer @tarekgh | Included:
  • System.Diagnostics.DiagnosticSource
  • System.Diagnostics.TraceSource
| From 004feb873271a7e5d9862b4b0dd1edb8e1db9543 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 10 Jul 2021 13:49:58 -0400 Subject: [PATCH 62/72] Fix erroneous "globalizationMode" local naming in PlatformDetection (#55457) --- .../tests/TestUtilities/System/PlatformDetection.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 5038c4f01977e..0bd29858a1692 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -242,14 +242,9 @@ public static string GetDistroVersionString() private static bool GetStaticNonPublicBooleanPropertyValue(string typeName, string propertyName) { - Type globalizationMode = Type.GetType(typeName); - if (globalizationMode != null) + if (Type.GetType(typeName)?.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod is MethodInfo mi) { - MethodInfo methodInfo = globalizationMode.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; - if (methodInfo != null) - { - return (bool)methodInfo.Invoke(null, null); - } + return (bool)mi.Invoke(null, null); } return false; From 2eb0071689ebe4157269d78cd1b7401b5f81f45b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 10 Jul 2021 20:38:44 +0200 Subject: [PATCH 63/72] use O_TRUNC when file locking is disabled, avoid ftruncate syscall (#55456) * add test project with file locking disabled via config file * use O_TRUNC when file locking is disabled, avoid ftruncate syscall * Apply suggestions from code review Co-authored-by: Stephen Toub --- .../TestUtilities/System/PlatformDetection.cs | 8 +++-- .../System.IO.FileSystem.sln | 7 ++++ .../DisabledFileLockingSwitchTests.cs | 16 ++++++++++ ...ileSystem.DisabledFileLocking.Tests.csproj | 32 +++++++++++++++++++ .../runtimeconfig.template.json | 5 +++ .../System.IO.FileSystem/tests/File/Copy.cs | 2 +- .../System.IO.FileSystem/tests/File/Create.cs | 3 +- .../tests/File/ReadWriteAllBytes.cs | 3 +- .../tests/File/ReadWriteAllBytesAsync.cs | 3 +- .../tests/File/ReadWriteAllLines.cs | 6 ++-- .../tests/File/ReadWriteAllLinesAsync.cs | 3 +- .../tests/File/ReadWriteAllText.cs | 3 +- .../tests/File/ReadWriteAllTextAsync.cs | 3 +- .../tests/FileStream/ctor_str_fm_fa_fs.cs | 3 +- .../FileStream/ctor_str_fm_fa_fs.write.cs | 3 +- ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 1 + .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 20 ++++++++++-- 17 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 0bd29858a1692..e11da7e8d4d93 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -70,8 +70,8 @@ public static partial class PlatformDetection public static bool IsLinqExpressionsBuiltWithIsInterpretingOnly => s_LinqExpressionsBuiltWithIsInterpretingOnly.Value; public static bool IsNotLinqExpressionsBuiltWithIsInterpretingOnly => !IsLinqExpressionsBuiltWithIsInterpretingOnly; private static readonly Lazy s_LinqExpressionsBuiltWithIsInterpretingOnly = new Lazy(GetLinqExpressionsBuiltWithIsInterpretingOnly); - private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() - { + private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() + { Type type = typeof(LambdaExpression); if (type != null) { @@ -285,6 +285,10 @@ private static Version GetICUVersion() public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value; + private static readonly Lazy s_fileLockingDisabled = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("Microsoft.Win32.SafeHandles.SafeFileHandle", "DisableFileLocking")); + + public static bool IsFileLockingEnabled => IsWindows || !s_fileLockingDisabled.Value; + private static bool GetIsInContainer() { if (IsWindows) diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 93f9297296e4e..fd94f8bfdcac8 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Net5Co EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{FEF03BCC-509F-4646-9132-9DE27FA3DA6F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.FileSystem.DisabledFileLocking.Tests", "tests\DisabledFileLockingTests\System.IO.FileSystem.DisabledFileLocking.Tests.csproj", "{D20CD3B7-A332-4D47-851A-FD8C80AE10B9}" +EndProject Global GlobalSection(NestedProjects) = preSolution {D350D6E7-52F1-40A4-B646-C178F6BBB689} = {1A727AF9-4F39-4109-BB8F-813286031DC9} @@ -43,6 +45,7 @@ Global {D7DF8034-3AE5-4DEF-BCC4-6353239391BF} = {D9FB1730-B750-4C0D-8D24-8C992DEB6034} {48E07F12-8597-40DE-8A37-CCBEB9D54012} = {1A727AF9-4F39-4109-BB8F-813286031DC9} {FEF03BCC-509F-4646-9132-9DE27FA3DA6F} = {1A727AF9-4F39-4109-BB8F-813286031DC9} + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9} = {1A727AF9-4F39-4109-BB8F-813286031DC9} EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -97,6 +100,10 @@ Global {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.Build.0 = Release|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs new file mode 100644 index 0000000000000..736991961ac96 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.Tests +{ + public class DisabledFileLockingSwitchTests + { + [Fact] + public static void ConfigSwitchIsHonored() + { + Assert.Equal(OperatingSystem.IsWindows(), PlatformDetection.IsFileLockingEnabled); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj new file mode 100644 index 0000000000000..87afaa0fb079f --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj @@ -0,0 +1,32 @@ + + + true + true + + $(NetCoreAppCurrent)-Unix + + --working-dir=/test-dir + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..c118f2a0a0a0b --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.IO.DisableFileLocking": true + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs index eb2686807bc1c..643d2a98cf22a 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs @@ -355,7 +355,7 @@ public void WindowsAlternateDataStreamOverwrite(string defaultStream, string alt /// /// Single tests that shouldn't be duplicated by inheritance. /// - [SkipOnPlatform(TestPlatforms.Browser, "https://github.com/dotnet/runtime/issues/40867 - flock not supported")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public sealed class File_Copy_Single : FileSystemTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem/tests/File/Create.cs b/src/libraries/System.IO.FileSystem/tests/File/Create.cs index bf0417dbfe061..fb7f4be892c74 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Create.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Create.cs @@ -142,8 +142,7 @@ public void InvalidDirectory() Assert.Throws(() => Create(testFile)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void FileInUse() { DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath()); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs index 7f13447f37d92..54c7643766335 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs @@ -80,8 +80,7 @@ public void Overwrite() Assert.Equal(overwriteBytes, File.ReadAllBytes(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs index 111ecb545adea..748c01dbffb8f 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs @@ -94,8 +94,7 @@ public async Task OverwriteAsync() Assert.Equal(overwriteBytes, await File.ReadAllBytesAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs index c0ba0787d577a..99c94b450790f 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs @@ -77,8 +77,7 @@ public virtual void Overwrite() Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); @@ -267,8 +266,7 @@ public virtual void Overwrite() Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs index 76d1541d60c5e..b2a1288639db3 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs @@ -75,8 +75,7 @@ public virtual async Task OverwriteAsync() Assert.Equal(overwriteLines, await ReadAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs index d24a8445e6d7f..1dbe303849658 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs @@ -84,8 +84,7 @@ public virtual void Overwrite() Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs index 3481bff97b4f4..3ad76c272fff6 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs @@ -83,8 +83,7 @@ public virtual async Task OverwriteAsync() Assert.Equal(overwriteLines, await ReadAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs index 386b2992ee3e8..205e2940e7ec3 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs @@ -120,10 +120,9 @@ public void FileShareOpenOrCreate() } } - [Theory] [InlineData(FileMode.Create)] [InlineData(FileMode.Truncate)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void NoTruncateOnFileShareViolation(FileMode fileMode) { string fileName = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs index ae93555bbe831..6ee1c6fc95c8b 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs @@ -37,8 +37,7 @@ public void FileShareWriteExisting() } } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void FileShareWithoutWriteThrows() { string fileName = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index dd67a63fed17a..745ff60368b2e 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 10dd1eabee010..ee1def85b9e02 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -206,15 +206,29 @@ private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mo { default: case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. - case FileMode.Truncate: // We truncate the file after getting the lock + break; + case FileMode.Truncate: + if (DisableFileLocking) + { + // if we don't lock the file, we can truncate it when opening + // otherwise we truncate the file after getting the lock + flags |= Interop.Sys.OpenFlags.O_TRUNC; + } break; case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later case FileMode.OpenOrCreate: - case FileMode.Create: // We truncate the file after getting the lock flags |= Interop.Sys.OpenFlags.O_CREAT; break; + case FileMode.Create: + flags |= Interop.Sys.OpenFlags.O_CREAT; + if (DisableFileLocking) + { + flags |= Interop.Sys.OpenFlags.O_TRUNC; + } + break; + case FileMode.CreateNew: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); break; @@ -292,7 +306,7 @@ private void Init(string path, FileMode mode, FileAccess access, FileShare share ignoreNotSupported: true); // just a hint. } - if (mode == FileMode.Create || mode == FileMode.Truncate) + if ((mode == FileMode.Create || mode == FileMode.Truncate) && !DisableFileLocking) { // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated // if opened successfully. From 960e7b352d5f4166193b2ca35c2f627ce0b60332 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Sat, 10 Jul 2021 13:01:37 -0700 Subject: [PATCH 64/72] Disable tests blocking CI (#55446) * Turn off test FlushAsync_ThrowsIfWriterReaderWithException * Turn off System.Net.NameResolution.Tests.GetHost* tests for SLES --- .../tests/FlushAsyncTests.cs | 2 +- .../FunctionalTests/GetHostByNameTest.cs | 15 ++++++++++++- .../tests/FunctionalTests/GetHostEntryTest.cs | 22 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs b/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs index 9647a47055b18..fa3db4c91ae4d 100644 --- a/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs +++ b/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs @@ -22,7 +22,7 @@ public void FlushAsync_ReturnsCompletedTaskWhenMaxSizeIfZero() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55435")] public async Task FlushAsync_ThrowsIfWriterReaderWithException() { void ThrowTestException() diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs index 94dba8bdb7fcd..2b62cb6c77192 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs @@ -4,6 +4,8 @@ #pragma warning disable 0618 // use of obsolete methods using System.Net.Sockets; + +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Net.NameResolution.Tests @@ -106,17 +108,28 @@ 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) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } IPHostEntry entry = Dns.GetHostByName(""); // DNS labels should be compared as case insensitive for ASCII characters. See RFC 4343. Assert.Contains(Dns.GetHostName(), entry.HostName, StringComparison.OrdinalIgnoreCase); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process), nameof(PlatformDetection.IsThreadingSupported))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void DnsObsoleteBeginEndGetHostByName_EmptyString_ReturnsHostName() { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + IPHostEntry entry = Dns.EndGetHostByName(Dns.BeginGetHostByName("", null, null)); // DNS labels should be compared as case insensitive for ASCII characters. See RFC 4343. diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index 6fa40dd80c46a..f860a2274e818 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Net.NameResolution.Tests @@ -21,13 +22,18 @@ public async Task Dns_GetHostEntryAsync_IPAddress_Ok() } [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] - + [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] [InlineData("")] [InlineData(TestSettings.LocalHost)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task Dns_GetHostEntry_HostString_Ok(string hostName) { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + try { await TestGetHostEntryAsync(() => Task.FromResult(Dns.GetHostEntry(hostName))); @@ -72,12 +78,20 @@ public async Task Dns_GetHostEntry_HostString_Ok(string hostName) } [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] [InlineData("")] [InlineData(TestSettings.LocalHost)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) => + public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) + { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(hostName)); + } [Fact] public async Task Dns_GetHostEntryAsync_IPString_Ok() => From fce412be7779966533b94ba879e98e4eecbd94fc Mon Sep 17 00:00:00 2001 From: Yauk <33248368+Yauk@users.noreply.github.com> Date: Sat, 10 Jul 2021 15:23:25 -0700 Subject: [PATCH 65/72] Support filtering ObjectAllocated callback for pinned object heap allocation only (#55448) * Prototype allocation profiler * Add callback for pinned objects * Fix the build issue caused by corprof.idl change * Improve the test * Misc changes for the tests Co-authored-by: Andrew Au Co-authored-by: Yauk Jia --- src/coreclr/inc/corprof.idl | 3 + src/coreclr/inc/profilepriv.inl | 15 ++++ src/coreclr/pal/prebuilt/inc/corprof.h | 1 + src/coreclr/vm/eeprofinterfaces.inl | 8 ++ src/coreclr/vm/gchelpers.cpp | 3 +- src/tests/profiler/gc/gcallocate.cs | 33 +++++++++ src/tests/profiler/gc/gcallocate.csproj | 23 ++++++ src/tests/profiler/native/CMakeLists.txt | 1 + src/tests/profiler/native/classfactory.cpp | 2 + .../gcallocateprofiler/gcallocateprofiler.cpp | 73 +++++++++++++++++++ .../gcallocateprofiler/gcallocateprofiler.h | 26 +++++++ .../gcbasicprofiler/gcbasicprofiler.cpp | 4 +- .../profiler/native/gcprofiler/gcprofiler.cpp | 2 +- 13 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 src/tests/profiler/gc/gcallocate.cs create mode 100644 src/tests/profiler/gc/gcallocate.csproj create mode 100644 src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp create mode 100644 src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h diff --git a/src/coreclr/inc/corprof.idl b/src/coreclr/inc/corprof.idl index 8fc965a84f6b5..d1c58f96cf97c 100644 --- a/src/coreclr/inc/corprof.idl +++ b/src/coreclr/inc/corprof.idl @@ -667,6 +667,9 @@ typedef enum COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x00000080, + // Enables the pinned object allocation monitoring. + COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x00000100, + COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS | COR_PRF_HIGH_BASIC_GC | diff --git a/src/coreclr/inc/profilepriv.inl b/src/coreclr/inc/profilepriv.inl index 08ba58f5623f1..e99591c5ffd18 100644 --- a/src/coreclr/inc/profilepriv.inl +++ b/src/coreclr/inc/profilepriv.inl @@ -1860,6 +1860,21 @@ inline BOOL CORProfilerTrackLargeAllocations() (&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED)); } +inline BOOL CORProfilerTrackPinnedAllocations() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + return + (CORProfilerPresent() && + (&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED)); +} + inline BOOL CORProfilerEnableRejit() { CONTRACTL diff --git a/src/coreclr/pal/prebuilt/inc/corprof.h b/src/coreclr/pal/prebuilt/inc/corprof.h index 85ce86870bf8c..e82623d0c09f0 100644 --- a/src/coreclr/pal/prebuilt/inc/corprof.h +++ b/src/coreclr/pal/prebuilt/inc/corprof.h @@ -574,6 +574,7 @@ enum __MIDL___MIDL_itf_corprof_0000_0000_0006 COR_PRF_HIGH_REQUIRE_PROFILE_IMAGE = 0, COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED = 0x40, COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x80, + COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x100, COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) , COR_PRF_HIGH_ALLOWABLE_NOTIFICATION_PROFILER = ( ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_DISABLE_TIERED_COMPILATION ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) , COR_PRF_HIGH_MONITOR_IMMUTABLE = COR_PRF_HIGH_DISABLE_TIERED_COMPILATION diff --git a/src/coreclr/vm/eeprofinterfaces.inl b/src/coreclr/vm/eeprofinterfaces.inl index 250b3700f801a..da6e978832968 100644 --- a/src/coreclr/vm/eeprofinterfaces.inl +++ b/src/coreclr/vm/eeprofinterfaces.inl @@ -31,5 +31,13 @@ FORCEINLINE BOOL TrackLargeAllocations() #endif // PROFILING_SUPPORTED } +FORCEINLINE BOOL TrackPinnedAllocations() +{ +#ifdef PROFILING_SUPPORTED + return CORProfilerTrackPinnedAllocations(); +#else + return FALSE; +#endif // PROFILING_SUPPORTED +} #endif diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 0cecfc624a744..01ffd5305d9f0 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -324,7 +324,8 @@ void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags) // Notify the profiler of the allocation // do this after initializing bounds so callback has size information if (TrackAllocations() || - (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP)) + (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP) || + (TrackPinnedAllocations() && flags & GC_ALLOC_PINNED_OBJECT_HEAP)) { OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); GCPROTECT_BEGIN(objref); diff --git a/src/tests/profiler/gc/gcallocate.cs b/src/tests/profiler/gc/gcallocate.cs new file mode 100644 index 0000000000000..b3fbfd9d29cfe --- /dev/null +++ b/src/tests/profiler/gc/gcallocate.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; + +namespace Profiler.Tests +{ + class GCAllocateTests + { + static readonly Guid GcAllocateEventsProfilerGuid = new Guid("55b9554d-6115-45a2-be1e-c80f7fa35369"); + + public static int RunTest(String[] args) + { + int[] large = new int[100000]; + int[] pinned = GC.AllocateArray(32, true); + Console.WriteLine("Test Passed"); + return 100; + } + + public static int Main(string[] args) + { + if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase)) + { + return RunTest(args); + } + + return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location, + testName: "GCCallbacksAllocate", + profilerClsid: GcAllocateEventsProfilerGuid); + } + } +} diff --git a/src/tests/profiler/gc/gcallocate.csproj b/src/tests/profiler/gc/gcallocate.csproj new file mode 100644 index 0000000000000..b2fbdc913b04a --- /dev/null +++ b/src/tests/profiler/gc/gcallocate.csproj @@ -0,0 +1,23 @@ + + + .NETCoreApp + exe + BuildAndRun + true + 0 + true + + true + + true + + + + + + + + diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index 9a0561c3e6d5b..e068fdc1ea3cb 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES eventpipeprofiler/eventpipereadingprofiler.cpp eventpipeprofiler/eventpipewritingprofiler.cpp eventpipeprofiler/eventpipemetadatareader.cpp + gcallocateprofiler/gcallocateprofiler.cpp gcbasicprofiler/gcbasicprofiler.cpp gcprofiler/gcprofiler.cpp getappdomainstaticaddress/getappdomainstaticaddress.cpp diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index b41f6ba7bc9ef..fe999926de5b7 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -6,6 +6,7 @@ #include "eventpipeprofiler/eventpipereadingprofiler.h" #include "eventpipeprofiler/eventpipewritingprofiler.h" #include "getappdomainstaticaddress/getappdomainstaticaddress.h" +#include "gcallocateprofiler/gcallocateprofiler.h" #include "gcbasicprofiler/gcbasicprofiler.h" #include "gcprofiler/gcprofiler.h" #include "metadatagetdispenser/metadatagetdispenser.h" @@ -61,6 +62,7 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI //A little simplistic, we create an instance of every profiler, then return the one whose CLSID matches Profiler* profilers[] = { + new GCAllocateProfiler(), new GCBasicProfiler(), new ReJITProfiler(), new EventPipeReadingProfiler(), diff --git a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp new file mode 100644 index 0000000000000..20e029704301f --- /dev/null +++ b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "gcallocateprofiler.h" + +GUID GCAllocateProfiler::GetClsid() +{ + // {55b9554d-6115-45a2-be1e-c80f7fa35369} + GUID clsid = { 0x55b9554d, 0x6115, 0x45a2,{ 0xbe, 0x1e, 0xc8, 0x0f, 0x7f, 0xa3, 0x53, 0x69 } }; + return clsid; +} + +HRESULT GCAllocateProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) +{ + Profiler::Initialize(pICorProfilerInfoUnk); + + HRESULT hr = S_OK; + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_ENABLE_OBJECT_ALLOCATED, COR_PRF_HIGH_BASIC_GC | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED | COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED))) + { + printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr); + return hr; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE GCAllocateProfiler::ObjectAllocated(ObjectID objectId, ClassID classId) +{ + COR_PRF_GC_GENERATION_RANGE gen; + HRESULT hr = pCorProfilerInfo->GetObjectGeneration(objectId, &gen); + if (FAILED(hr)) + { + printf("GetObjectGeneration failed hr=0x%x\n", hr); + _failures++; + } + else if (gen.generation == COR_PRF_GC_LARGE_OBJECT_HEAP) + { + _gcLOHAllocations++; + } + else if (gen.generation == COR_PRF_GC_PINNED_OBJECT_HEAP) + { + _gcPOHAllocations++; + } + else + { + printf("Unexpected object allocation captured, gen.generation=0x%x\n", gen.generation); + _failures++; + } + + return S_OK; +} + +HRESULT GCAllocateProfiler::Shutdown() +{ + Profiler::Shutdown(); + if (_gcPOHAllocations == 0) + { + printf("There is no POH allocations\n"); + } + else if (_gcLOHAllocations == 0) + { + printf("There is no LOH allocations\n"); + } + else if (_failures == 0) + { + printf("%d LOH objects allocated\n", (int)_gcLOHAllocations); + printf("%d POH objects allocated\n", (int)_gcPOHAllocations); + printf("PROFILER TEST PASSES\n"); + } + fflush(stdout); + + return S_OK; +} diff --git a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h new file mode 100644 index 0000000000000..65fb3b16240e0 --- /dev/null +++ b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "../profiler.h" + +class GCAllocateProfiler : public Profiler +{ +public: + GCAllocateProfiler() : Profiler(), + _gcLOHAllocations(0), + _gcPOHAllocations(0), + _failures(0) + {} + + virtual GUID GetClsid(); + virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); + virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId); + virtual HRESULT STDMETHODCALLTYPE Shutdown(); + +private: + std::atomic _gcLOHAllocations; + std::atomic _gcPOHAllocations; + std::atomic _failures; +}; diff --git a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp index a9f28011eb02b..6d377e6d115be 100644 --- a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp +++ b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp @@ -15,7 +15,7 @@ HRESULT GCBasicProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) Profiler::Initialize(pICorProfilerInfoUnk); HRESULT hr = S_OK; - if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, 0x10))) + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, COR_PRF_HIGH_BASIC_GC))) { _failures++; printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr); @@ -31,7 +31,7 @@ HRESULT GCBasicProfiler::Shutdown() if (_gcStarts == 0) { - printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n"); + printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n"); } else if (_gcFinishes == 0) { diff --git a/src/tests/profiler/native/gcprofiler/gcprofiler.cpp b/src/tests/profiler/native/gcprofiler/gcprofiler.cpp index 0fa298f59f3b9..42666a2d8eac8 100644 --- a/src/tests/profiler/native/gcprofiler/gcprofiler.cpp +++ b/src/tests/profiler/native/gcprofiler/gcprofiler.cpp @@ -31,7 +31,7 @@ HRESULT GCProfiler::Shutdown() if (_gcStarts == 0) { - printf("GCProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n"); + printf("GCProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n"); } else if (_gcFinishes == 0) { From 8fdc82909aefb4768ddce545d5352958e4874f8e Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sat, 10 Jul 2021 15:55:27 -0700 Subject: [PATCH 66/72] Propagators Support (#55419) --- ...em.Diagnostics.DiagnosticSourceActivity.cs | 15 +- ...System.Diagnostics.DiagnosticSource.csproj | 4 + .../System/Diagnostics/LegacyPropagator.cs | 189 +++++ .../System/Diagnostics/NoOutputPropagator.cs | 23 + .../Diagnostics/PassThroughPropagator.cs | 59 ++ .../System/Diagnostics/TextMapPropagator.cs | 141 ++++ .../tests/PropagatorTests.cs | 664 ++++++++++++++++++ ....Diagnostics.DiagnosticSource.Tests.csproj | 1 + 8 files changed, 1095 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 482a58bb414ea..7e50f732e4217 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -253,7 +253,20 @@ public sealed class ActivityListener : IDisposable public System.Diagnostics.SampleActivity? SampleUsingParentId { get { throw null; } set { throw null; } } public System.Diagnostics.SampleActivity? Sample { get { throw null; } set { throw null; } } public void Dispose() { throw null; } - } + } + public abstract class TextMapPropagator + { + public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out System.Collections.Generic.IEnumerable? fieldValues); + public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue); + public abstract System.Collections.Generic.IReadOnlyCollection Fields { get; } + public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter); + public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState); + public abstract System.Collections.Generic.IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter); + public static TextMapPropagator Current { get; set; } + public static TextMapPropagator CreateDefaultPropagator() { throw null; } + public static TextMapPropagator CreatePassThroughPropagator() { throw null; } + public static TextMapPropagator CreateNoOutputPropagator() { throw null; } + } } namespace System.Diagnostics.Metrics diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index e1dca02a19d4a..e4a9cbd28d7ff 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -39,7 +39,11 @@ + + + + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs new file mode 100644 index 0000000000000..a469dd5b56b6b --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace System.Diagnostics +{ + internal sealed class LegacyPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new LegacyPropagator(); + + public override IReadOnlyCollection Fields { get; } = new ReadOnlyCollection(new[] { TraceParent, RequestId, TraceState, Baggage, CorrelationContext }); + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (activity is null || setter is null) + { + return; + } + + string? id = activity.Id; + if (id is null) + { + return; + } + + if (activity.IdFormat == ActivityIdFormat.W3C) + { + setter(carrier, TraceParent, id); + if (!string.IsNullOrEmpty(activity.TraceStateString)) + { + setter(carrier, TraceState, activity.TraceStateString); + } + } + else + { + setter(carrier, RequestId, id); + } + + InjectBaggage(carrier, activity.Baggage, setter); + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) + { + if (getter is null) + { + traceId = null; + traceState = null; + return; + } + + getter(carrier, TraceParent, out traceId, out _); + if (traceId is null) + { + getter(carrier, RequestId, out traceId, out _); + } + + getter(carrier, TraceState, out traceState, out _); + } + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) + { + if (getter is null) + { + return null; + } + + getter(carrier, Baggage, out string? theBaggage, out _); + + IEnumerable>? baggage = null; + if (theBaggage is null || !TryExtractBaggage(theBaggage, out baggage)) + { + getter(carrier, CorrelationContext, out theBaggage, out _); + if (theBaggage is not null) + { + TryExtractBaggage(theBaggage, out baggage); + } + } + + return baggage; + } + + internal static bool TryExtractBaggage(string baggageString, out IEnumerable>? baggage) + { + baggage = null; + List>? baggageList = null; + + if (string.IsNullOrEmpty(baggageString)) + { + return true; + } + + int currentIndex = 0; + + do + { + // Skip spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // No Key exist + } + + int keyStart = currentIndex; + + // Search end of the key + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab && baggageString[currentIndex] != '=') + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; + } + + int keyEnd = currentIndex; + + if (baggageString[currentIndex] != '=') + { + // Skip Spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // Wrong key format + } + + if (baggageString[currentIndex] != '=') + { + break; // wrong key format. + } + } + + currentIndex++; + + // Skip spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // Wrong value format + } + + int valueStart = currentIndex; + + // Search end of the value + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab && + baggageString[currentIndex] != Comma && baggageString[currentIndex] != Semicolon) + { + currentIndex++; + } + + if (keyStart < keyEnd && valueStart < currentIndex) + { + baggageList ??= new(); + + // Insert in reverse order for asp.net compatability. + baggageList.Insert(0, new KeyValuePair( + WebUtility.UrlDecode(baggageString.Substring(keyStart, keyEnd - keyStart)).Trim(s_trimmingSpaceCharacters), + WebUtility.UrlDecode(baggageString.Substring(valueStart, currentIndex - valueStart)).Trim(s_trimmingSpaceCharacters))); + } + + // Skip to end of values + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Comma) + { + currentIndex++; + } + + currentIndex++; // Move to next key-value entry + } while (currentIndex < baggageString.Length); + + baggage = baggageList; + return baggageList != null; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs new file mode 100644 index 0000000000000..f9d503a8f1c9a --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Diagnostics +{ + internal sealed class NoOutputPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new NoOutputPropagator(); + + public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + // nothing to do. + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter); + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs new file mode 100644 index 0000000000000..01bf821a5cbc9 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Diagnostics +{ + internal sealed class PassThroughPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new PassThroughPropagator(); + + public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (setter is null) + { + return; + } + + GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage); + if (parentId is null) + { + return; + } + + setter(carrier, isW3c ? TraceParent : RequestId, parentId); + + if (!string.IsNullOrEmpty(traceState)) + { + setter(carrier, TraceState, traceState); + } + + if (baggage is not null) + { + InjectBaggage(carrier, baggage, setter); + } + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter); + + private static void GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage) + { + Activity? activity = Activity.Current; + + while (activity?.Parent is Activity parent) + { + activity = parent; + } + + traceState = activity?.TraceStateString; + parentId = activity?.ParentId ?? activity?.Id; + isW3c = parentId is not null ? Activity.TryConvertIdToContext(parentId, traceState, out _) : false; + baggage = activity?.Baggage; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs new file mode 100644 index 0000000000000..0dab622a29833 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Diagnostics +{ + /// + /// An implementation of TextMapPropagator determines if and how distributed context information is encoded and decoded as it traverses the network. + /// The encoding can be transported over any network protocol that supports string key-value pairs. For example when using HTTP, each key value pair is an HTTP header. + /// TextMapPropagator inject values into and extracts values from carriers as string key/value pairs. + /// + public abstract class TextMapPropagator + { + private static TextMapPropagator s_current = CreateDefaultPropagator(); + + /// + /// The callback that is used in propagators' extract methods. The callback is invoked to lookup the value of a named field. + /// + /// Carrier is the medium used by Propagators to read values from. + /// The propagation field name. + /// An output string to receive the value corresponds to the input fieldName. This should return non null value if there is only one value for the input field name. + /// An output collection of strings to receive the values corresponds to the input fieldName. This should return non null value if there are more than one value for the input field name. + public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues); + + /// + /// The callback that is used in propagators' inject methods. This callback is invoked to set the value of a named field. + /// Propagators may invoke it multiple times in order to set multiple fields. + /// + /// Carrier is the medium used by Propagators to write values to. + /// The propagation field name. + /// The value corresponds to the input fieldName. + public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue); + + /// + /// The set of field names this propagator is likely to read or write. + /// + /// Returns list of fields that will be used by the TextMapPropagator. + public abstract IReadOnlyCollection Fields { get; } + + /// + /// Injects the trace values stroed in the object into a carrier. For example, into the headers of an HTTP request. + /// + /// The Activity object has the distributed context to inject to the carrier. + /// Carrier is the medium in which the distributed context will be stored. + /// The callback will be invoked to set a named key/value pair on the carrier. + public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter); + + /// + /// Extracts trace Id and trace state from an incoming request represented by the carrier. For example, from the headers of an HTTP request. + /// + /// Carrier is the medium from which values will be read. + /// The callback will be invoked to get the propagation trace Id and trace state from carrier. + /// The extracted trace Id from the carrier. + /// The extracted trace state from the carrier. + public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState); + + /// + /// Extracts the baggage key-value pair list from an incoming request represented by the carrier. For example, from the headers of an HTTP request. + /// + /// Carrier is the medium from which values will be read. + /// The callback will be invoked to get the propagation baggage list from carrier. + /// Returns the extracted key-value pair list from the carrier. + public abstract IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter); + + /// + /// Get or set the process wide propagator object which used as the current selected propagator. + /// + public static TextMapPropagator Current + { + get + { + Debug.Assert(s_current is not null); + return s_current; + } + + set + { + s_current = value ?? throw new ArgumentNullException(nameof(value)); + } + } + + /// + /// returns the default propagator object which Current property will be initialized with. + /// + /// + /// CreateDefaultPropagator will create a propagator instance that can inject and extract the headers with field names "tarcestate", + /// "traceparent" of the identifiers which are formatted as W3C trace parent, "Request-Id" of the identifiers which are formatted as a hierarchical identifier. + /// The returned propagator can inject the baggage key-value pair list with header name "Correlation-Context" and it can extract the baggage values mapped to header names "Correlation-Context" and "baggage". + /// + public static TextMapPropagator CreateDefaultPropagator() => LegacyPropagator.Instance; + + /// + /// Returns a propagator which attempts to act transparently, emitting the same data on outbound network requests that was received on the in-bound request. + /// When encoding the outbound message, this propagator uses information from the request's root Activity, ignoring any intermediate Activities that may have been created while processing the request. + /// + public static TextMapPropagator CreatePassThroughPropagator() => PassThroughPropagator.Instance; + + /// + /// Returns a propagator which does not transmit any distributed context information in outbound network messages. + /// + public static TextMapPropagator CreateNoOutputPropagator() => NoOutputPropagator.Instance; + + // internal stuff + + internal static void InjectBaggage(object? carrier, IEnumerable> baggage, PropagatorSetterCallback setter) + { + using (IEnumerator> e = baggage.GetEnumerator()) + { + if (e.MoveNext()) + { + StringBuilder baggageList = new StringBuilder(); + + do + { + KeyValuePair item = e.Current; + baggageList.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(CommaWithSpace); + } while (e.MoveNext()); + + setter(carrier, CorrelationContext, baggageList.ToString(0, baggageList.Length - 2)); + } + } + } + + internal const string TraceParent = "traceparent"; + internal const string RequestId = "Request-Id"; + internal const string TraceState = "tracestate"; + internal const string Baggage = "baggage"; + internal const string CorrelationContext = "Correlation-Context"; + internal const char Space = ' '; + internal const char Tab = (char)9; + internal const char Comma = ','; + internal const char Semicolon = ';'; + internal const string CommaWithSpace = ", "; + + internal static readonly char [] s_trimmingSpaceCharacters = new char[] { Space, Tab }; + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs new file mode 100644 index 0000000000000..a0ade495467da --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs @@ -0,0 +1,664 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Linq; +using System.Collections.Generic; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public class PropagatorTests + { + internal const string TraceParent = "traceparent"; + internal const string RequestId = "Request-Id"; + internal const string TraceState = "tracestate"; + internal const string Baggage = "baggage"; + internal const string CorrelationContext = "Correlation-Context"; + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestAllPropagators() + { + RemoteExecutor.Invoke(() => { + Assert.NotNull(TextMapPropagator.Current); + + // + // Default Propagator + // + + Assert.Same(TextMapPropagator.CreateDefaultPropagator(), TextMapPropagator.Current); + + TestDefaultPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "Legacy1=true", + new List>() { new KeyValuePair(" LegacyKey1 ", " LegacyValue1 ") }); + + TestDefaultPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "Legacy2=true", + new List>() { new KeyValuePair("LegacyKey2", "LegacyValue2") }); + + TestFields(TextMapPropagator.Current); + + // + // NoOutput Propagator + // + + TextMapPropagator.Current = TextMapPropagator.CreateNoOutputPropagator(); + Assert.NotNull(TextMapPropagator.Current); + TestNoOutputPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "ActivityState=1", + new List>() { new KeyValuePair("B1", "V1"), new KeyValuePair(" B2 ", " V2 ")}); + + TestNoOutputPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "ActivityState=2", + null); + + TestNoOutputPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "ActivityState=1", + new List>() { new KeyValuePair(" B3 ", " V3"), new KeyValuePair(" B4 ", " V4 "), new KeyValuePair("B5", "V5")}); + + TestNoOutputPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "ActivityState=2", + null); + + TestFields(TextMapPropagator.Current); + + // + // Pass Through Propagator + // + + TextMapPropagator.Current = TextMapPropagator.CreatePassThroughPropagator(); + Assert.NotNull(TextMapPropagator.Current); + TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain( + TextMapPropagator.Current, + "PassThrough=true", + new List>() { new KeyValuePair("PassThroughKey1", "PassThroughValue1"), new KeyValuePair("PassThroughKey2", "PassThroughValue2")}); + + TestPassThroughPropagatorUsingHierarchicalActivityWithParentId( + TextMapPropagator.Current, + "PassThrough1=true", + new List>() { new KeyValuePair("PassThroughKey3", "PassThroughValue3"), new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ")}); + + TestPassThroughPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "PassThrough2=1", + new List>() { new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ") }); + + TestPassThroughPropagatorWithNullCurrent(TextMapPropagator.Current); + + TestFields(TextMapPropagator.Current); + + // + // Test Current + // + + Assert.Throws(() => TextMapPropagator.Current = null); + + }).Dispose(); + } + + private void TestDefaultPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("LegacyW3C1", "LegacyW3CState=1", baggage); + using Activity b = CreateW3CActivity("LegacyW3C2", "LegacyW3CState=2", baggage); + + Assert.NotSame(Activity.Current, a); + + TestDefaultPropagatorUsing(a, propagator, state, baggage); + + Assert.Same(Activity.Current, b); + + TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); + } + + private void TestDefaultPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("LegacyHierarchical1", null, "LegacyHierarchicalState=1", baggage); + using Activity b = CreateHierarchicalActivity("LegacyHierarchical2", null, "LegacyHierarchicalState=2", baggage); + + Assert.NotSame(Activity.Current, a); + + TestDefaultPropagatorUsing(a, propagator, state, baggage); + + Assert.Same(Activity.Current, b); + + TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); + } + + private void TestDefaultPropagatorUsing(Activity a, TextMapPropagator propagator, string state, IEnumerable> baggage) + { + // Test with non-current + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent && a.IdFormat == ActivityIdFormat.W3C) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == RequestId && a.IdFormat != ActivityIdFormat.W3C) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestBaggageExtraction(propagator, a); + } + + private void TestNoOutputPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("NoOutputHierarchical", null, state, baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"Not expected to have the setter callback be called in the NoOutput propgator."); + }); + + TestDefaultExtraction(propagator, a); + + TestBaggageExtraction(propagator, a); + } + + private void TestNoOutputPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("NoOutputW3C", state, baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"Not expected to have the setter callback be called in the NoOutput propgator."); + }); + + TestDefaultExtraction(propagator, a); + + TestBaggageExtraction(propagator, a); + } + + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("PassThrough", null, state, baggage); + using Activity b = CreateHierarchicalActivity("PassThroughChild1", null, state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); + using Activity c = CreateHierarchicalActivity("PassThroughChild2", null, state + "2", new List>() { new KeyValuePair("Child2Key", "Child2Value") } ); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.False(true, $"Unexpected to inject a TraceParent with Hierarchical Activity."); + return; + } + + if (fieldName == RequestId) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestDefaultExtraction(propagator, b); + TestDefaultExtraction(propagator, c); + + TestBaggageExtraction(propagator, a); + TestBaggageExtraction(propagator, b); + TestBaggageExtraction(propagator, c); + } + + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentId(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("PassThrough", "Parent1", state, baggage); + using Activity b = CreateHierarchicalActivity("PassThroughChild1", "Parent2", state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); + using Activity c = CreateHierarchicalActivity("PassThroughChild2", "Parent3", state + "2", new List>() { new KeyValuePair("Child2Key", "Child2Value") } ); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.False(true, $"Unexpected to inject a TraceParent with Hierarchical Activity."); + return; + } + + if (fieldName == RequestId) + { + Assert.Equal(c.ParentId, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(c.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(c.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestDefaultExtraction(propagator, b); + TestDefaultExtraction(propagator, c); + + TestBaggageExtraction(propagator, a); + TestBaggageExtraction(propagator, b); + TestBaggageExtraction(propagator, c); + } + + private void TestPassThroughPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("PassThroughW3C", "PassThroughW3CState=1", baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestBaggageExtraction(propagator, a); + } + + private void TestPassThroughPropagatorWithNullCurrent(TextMapPropagator propagator) + { + Activity.Current = null; + + propagator.Inject(null, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"PassThroughPropagator shouldn't inject anything if the Activity.Current is null"); + }); + + using Activity a = CreateW3CActivity("PassThroughNotNull", "", null); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.Equal(a.Id, value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + } + + private void TestDefaultExtraction(TextMapPropagator propagator, Activity a) + { + bool traceParentEncountered = false; + + propagator.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValues = null; + fieldValue = null; + + if (fieldName == TraceParent) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + fieldValue = a.Id; + } + else + { + traceParentEncountered = true; + } + return; + } + + if (fieldName == RequestId) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + Assert.True(false, $"Not expected to get RequestId as we expect the request handled using TraceParenet."); + } + else + { + Assert.True(traceParentEncountered, $"Expected to get TraceParent request before getting RequestId."); + fieldValue = a.Id; + } + + return; + } + + if (fieldName == TraceState) + { + fieldValue = a.TraceStateString; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }, out string? traceId, out string? traceState); + + Assert.Equal(a.Id, traceId); + Assert.Equal(a.TraceStateString, traceState); + } + + private void TestBaggageExtraction(TextMapPropagator propagator, Activity a) + { + bool baggageEncountered = false; + + IEnumerable>? b = propagator.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValue = null; + fieldValues = null; + + if (fieldName == Baggage) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + fieldValue = GetFormattedBaggage(a.Baggage); + } + else + { + baggageEncountered = true; + } + + return; + } + + if (fieldName == CorrelationContext && a.IdFormat != ActivityIdFormat.W3C) + { + Assert.True(baggageEncountered, $"Expected to get Baggage request before getting Correlation-Context."); + fieldValue = GetFormattedBaggage(a.Baggage); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + Assert.Equal(GetFormattedBaggage(a.Baggage, false, true), GetFormattedBaggage(b, true)); + } + + private void TestFields(TextMapPropagator propagator) + { + Assert.True(propagator.Fields.Contains(TraceParent)); + Assert.True(propagator.Fields.Contains(RequestId)); + Assert.True(propagator.Fields.Contains(TraceState)); + Assert.True(propagator.Fields.Contains(Baggage)); + Assert.True(propagator.Fields.Contains(CorrelationContext)); + } + + internal static string GetFormattedBaggage(IEnumerable>? b, bool flipOrder = false, bool trimSpaces = false) + { + string formattedBaggage = ""; + + if (b is null) + { + return formattedBaggage; + } + List> list = new List>(b); + + int startIndex = flipOrder ? list.Count - 1 : 0; + int exitIndex = flipOrder ? -1 : list.Count; + int step = flipOrder ? -1 : 1; + + for (int i = startIndex; i != exitIndex; i += step) + { + string key = trimSpaces ? list[i].Key.Trim() : list[i].Key; + string value = trimSpaces ? list[i].Value.Trim() : list[i].Value; + + formattedBaggage += (formattedBaggage.Length > 0 ? ", " : "") + WebUtility.UrlEncode(key) + "=" + WebUtility.UrlEncode(value); + } + + return formattedBaggage; + } + + private Activity CreateHierarchicalActivity(string name, string parentId, string state, IEnumerable>? baggage) + { + Activity a = new Activity(name); + a.SetIdFormat(ActivityIdFormat.Hierarchical); + + if (baggage is not null) + { + foreach (KeyValuePair kvp in baggage) + { + a.SetBaggage(kvp.Key, kvp.Value); + } + } + + a.TraceStateString = state; + + if (parentId is not null) + { + a.SetParentId(parentId); + } + a.Start(); + + return a; + } + + private Activity CreateW3CActivity(string name, string state, IEnumerable>? baggage) + { + Activity a = new Activity(name); + a.SetIdFormat(ActivityIdFormat.W3C); + + if (baggage is not null) + { + foreach (KeyValuePair kvp in baggage) + { + a.SetBaggage(kvp.Key, kvp.Value); + } + } + + a.TraceStateString = state; + a.Start(); + + return a; + } + + internal static IEnumerable>? ParseBaggage(string baggageString) + { + if (baggageString is null) + { + return null; + } + + List> list = new(); + string [] parts = baggageString.Split(','); + + foreach (string part in parts) + { + string [] baggageItem = part.Split('='); + + if (baggageItem.Length != 2) + { + return null; // Invalid format + } + + list.Add(new KeyValuePair(WebUtility.UrlDecode(baggageItem[0]).Trim(), WebUtility.UrlDecode(baggageItem[1]).Trim())); + } + + return list; + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestCustomPropagator() + { + RemoteExecutor.Invoke(() => { + + TextMapPropagator.Current = new CustomPropagator(); + using Activity a = CreateW3CActivity("CustomW3C1", "CustomW3CState=1", new List>() { new KeyValuePair(" CustomKey1 ", " CustomValue1 ") }); + + string traceParent = "x-" + a.Id ; + string traceState = "x-" + a.TraceStateString; + string baggageString = "x=y, " + GetFormattedBaggage(a.Baggage); + + TextMapPropagator.Current.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == CustomPropagator.XTraceParent) + { + Assert.Equal(traceParent, value); + return; + } + + if (fieldName == CustomPropagator.XTraceState) + { + Assert.Equal(traceState, value); + return; + } + + if (fieldName == CustomPropagator.XBaggage) + { + Assert.Equal(baggageString, value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in the Custom Propagator"); + }); + + TextMapPropagator.Current.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + fieldValues = null; + fieldValue = null; + + if (fieldName == CustomPropagator.XTraceParent) + { + fieldValue = traceParent; + return; + } + + if (fieldName == CustomPropagator.XTraceState) + { + fieldValue = traceState; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in the Custom propagator"); + }, out string? traceId, out string? state); + + Assert.Equal(traceParent, traceId); + Assert.Equal(traceState, state); + + IEnumerable>? b = TextMapPropagator.Current.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValue = null; + fieldValues = null; + + if (fieldName == CustomPropagator.XBaggage) + { + fieldValue = baggageString; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in custom propagator"); + }); + + Assert.Equal(2, b.Count()); + Assert.Equal(new KeyValuePair("x", "y"), b.ElementAt(0)); + Assert.Equal(new KeyValuePair("CustomKey1", "CustomValue1"), b.ElementAt(1)); + + }).Dispose(); + } + + internal class CustomPropagator : TextMapPropagator + { + internal const string XTraceParent = "x-traceparent"; + internal const string XTraceState = "x-tracestate"; + internal const string XBaggage = "x-baggage"; + + public override IReadOnlyCollection Fields { get; } = new[] { XTraceParent, XTraceState, XBaggage}; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (activity is null || carrier is null) + { + return; + } + + setter(carrier, XTraceParent, "x-" + activity.Id); + + if (!string.IsNullOrEmpty(activity.TraceStateString)) + { + setter(carrier, XTraceState, "x-" + activity.TraceStateString); + } + + if (activity.Baggage.Count() > 0) + { + setter(carrier, XBaggage, "x=y, " + PropagatorTests.GetFormattedBaggage(activity.Baggage)); + } + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) + { + if (getter is null) + { + traceId = null; + traceState = null; + return; + } + + getter(carrier, XTraceParent, out traceId, out _); + getter(carrier, XTraceState, out traceState, out _); + } + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) + { + if (getter is null) + { + return null; + } + + getter(carrier, XBaggage, out string? theBaggage, out _); + + return PropagatorTests.ParseBaggage(theBaggage); + } + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj index 3d38a94931789..24c84423e6638 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj @@ -28,6 +28,7 @@ + From 21c251623d45e2a753cd687c0551ec327f837d51 Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Sat, 10 Jul 2021 17:10:48 -0700 Subject: [PATCH 67/72] Fixing MetricEventSource tests (#55385) * Fixing MetricEventSource tests I've seen two recent non-deterministic failures: 1. After starting the EventListener there is a delay of one collection interval (previously 0.3 seconds) where we expect calls to counter.Add and Histogram.Record() to update stats. On a fast machine this code would run in under a millisecond but in some test runs it still wasn't happening fast enough. 2. We were seeing error events from a previous test get observed in a later test because session id was being ignored for error events. Fixes: 1. Added an initial collection interval where no counter APIs will be invoked and the test will delay up to 60 seconds waiting for this. Hopefully this delay makes it more likely that the Add/Record APIs are ready to execute promptly once the 2nd interval begins. I also increased the intervals to 1 second. If that still isn't sufficient we can either further increase the collection intervals or disable the tests. I also moved these tests to outerloop because they are slow and noisy on the console, but it may have a side benefit lessening the impact if there are future timing related failures. 2. Tightened up the session id matching so only error events with empty id or the expected id are allowed. --- .../tests/MetricEventSourceTests.cs | 186 ++++++++++-------- 1 file changed, 103 insertions(+), 83 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs index c63aaa1a8712f..9cd9501d8c5e9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -16,6 +16,8 @@ namespace System.Diagnostics.Metrics.Tests public class MetricEventSourceTests { ITestOutputHelper _output; + const double IntervalSecs = 1; + static readonly TimeSpan s_waitForEventTimeout = TimeSpan.FromSeconds(60); public MetricEventSourceTests(ITestOutputHelper output) { @@ -23,6 +25,7 @@ public MetricEventSourceTests(ITestOutputHelper output) } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithEmptyMetadata() { using Meter meter = new Meter("TestMeter1"); @@ -34,15 +37,15 @@ public void EventSourcePublishesTimeSeriesWithEmptyMetadata() Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter1")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter1")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -52,10 +55,11 @@ public void EventSourcePublishesTimeSeriesWithEmptyMetadata() AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithMetadata() { using Meter meter = new Meter("TestMeter2"); @@ -67,28 +71,29 @@ public void EventSourcePublishesTimeSeriesWithMetadata() Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter2")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter2")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesForLateMeter() { // this ensures the MetricsEventSource exists when the listener tries to query @@ -102,10 +107,9 @@ public void EventSourcePublishesTimeSeriesForLateMeter() Histogram h; EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter3")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter3")) { - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); // the Meter is created after the EventSource was already monitoring meter = new Meter("TestMeter3"); @@ -118,10 +122,10 @@ public void EventSourcePublishesTimeSeriesForLateMeter() c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -131,7 +135,7 @@ public void EventSourcePublishesTimeSeriesForLateMeter() AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } finally { @@ -140,6 +144,7 @@ public void EventSourcePublishesTimeSeriesForLateMeter() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesForLateInstruments() { // this ensures the MetricsEventSource exists when the listener tries to query @@ -150,10 +155,9 @@ public void EventSourcePublishesTimeSeriesForLateInstruments() Histogram h; EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter4")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter4")) { - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); // Instruments are created after the EventSource was already monitoring c = meter.CreateCounter("counter1"); @@ -165,10 +169,10 @@ public void EventSourcePublishesTimeSeriesForLateInstruments() c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -178,10 +182,11 @@ public void EventSourcePublishesTimeSeriesForLateInstruments() AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithTags() { using Meter meter = new Meter("TestMeter5"); @@ -209,20 +214,21 @@ public void EventSourcePublishesTimeSeriesWithTags() Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter5")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter5")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5, new KeyValuePair("Color", "red")); c.Add(6, new KeyValuePair("Color", "blue")); h.Record(19, new KeyValuePair("Size", 123)); h.Record(20, new KeyValuePair("Size", 124)); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12, new KeyValuePair("Color", "red")); c.Add(13, new KeyValuePair("Color", "blue")); h.Record(26, new KeyValuePair("Size", 123)); h.Record(27, new KeyValuePair("Size", 124)); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -236,11 +242,12 @@ public void EventSourcePublishesTimeSeriesWithTags() AssertGaugeEventsPresent(events, meter.Name, og.Name, "Color=blue,Size=4", "", "18", "36"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=123", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=124", "", "0.5=20;0.95=20;0.99=20", "0.5=27;0.95=27;0.99=27"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceFiltersInstruments() { using Meter meterA = new Meter("TestMeterA"); @@ -257,10 +264,11 @@ public void EventSourceFiltersInstruments() Counter c3c = meterC.CreateCounter("counter3"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeterA\\counter3;TestMeterB\\counter1;TestMeterC\\counter2;TestMeterB;TestMeterC\\counter3")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c1a.Add(1); c2a.Add(1); c3a.Add(1); @@ -270,7 +278,7 @@ public void EventSourceFiltersInstruments() c1c.Add(1); c2c.Add(1); c3c.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c1a.Add(2); c2a.Add(2); @@ -281,7 +289,7 @@ public void EventSourceFiltersInstruments() c1c.Add(2); c2c.Add(2); c3c.Add(2); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -296,10 +304,11 @@ public void EventSourceFiltersInstruments() AssertCounterEventsNotPresent(events, meterA.Name, c1a.Name, ""); AssertCounterEventsNotPresent(events, meterA.Name, c2a.Name, ""); AssertCounterEventsNotPresent(events, meterC.Name, c1c.Name, ""); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesMissingDataPoints() { using Meter meter = new Meter("TestMeter6"); @@ -310,7 +319,7 @@ public void EventSourcePublishesMissingDataPoints() { counterState += 7; counterCollectInterval++; - if ((counterCollectInterval % 2) == 1) + if ((counterCollectInterval % 2) == 0) { return new Measurement[] { new Measurement(counterState) }; } @@ -326,7 +335,7 @@ public void EventSourcePublishesMissingDataPoints() { gaugeState += 9; gaugeCollectInterval++; - if ((gaugeCollectInterval % 2) == 1) + if ((gaugeCollectInterval % 2) == 0) { return new Measurement[] { new Measurement(gaugeState) }; } @@ -339,19 +348,20 @@ public void EventSourcePublishesMissingDataPoints() Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter6")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter6")) { + // no measurements in interval 1 + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); - // no measurements in interval 2 - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + // no measurements in interval 3 + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); - // no measurements in interval 4 - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 4); + listener.WaitForCollectionStop(s_waitForEventTimeout, 4); + // no measurements in interval 5 + listener.WaitForCollectionStop(s_waitForEventTimeout, 5); events = listener.Events.ToArray(); } @@ -359,12 +369,13 @@ public void EventSourcePublishesMissingDataPoints() AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "0", "12"); AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "0", "14", "0"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "", "27", ""); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "18", "", "36", ""); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "", "0.5=26;0.95=26;0.99=26", ""); - AssertCollectStartStopEventsPresent(events, intervalSecs, 4); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 5); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesEndEventsOnNewListener() { using Meter meter = new Meter("TestMeter7"); @@ -376,32 +387,33 @@ public void EventSourcePublishesEndEventsOnNewListener() Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter7")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter7")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); // some alternate listener starts listening - using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "ADifferentMeter"); - listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 4); + using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "ADifferentMeter"); + listener.WaitForEndInstrumentReporting(s_waitForEventTimeout, 4); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); AssertEndInstrumentReportingEventsPresent(events, c, oc, og, h); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesEndEventsOnMeterDispose() { using Meter meterA = new Meter("TestMeter8"); @@ -414,36 +426,37 @@ public void EventSourcePublishesEndEventsOnMeterDispose() Histogram h = meterB.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter8;TestMeter9")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter8;TestMeter9")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); meterA.Dispose(); - listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 3); + listener.WaitForEndInstrumentReporting(s_waitForEventTimeout, 3); h.Record(21); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 4); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meterA.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meterB.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26", "0.5=21;0.95=21;0.99=21"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 4); AssertEndInstrumentReportingEventsPresent(events, c, oc, og); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesInstruments() { using Meter meterA = new Meter("TestMeter10"); @@ -458,7 +471,7 @@ public void EventSourcePublishesInstruments() EventWrittenEventArgs[] events; using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.InstrumentPublishing, null, "")) { - listener.WaitForEnumerationComplete(TimeSpan.FromSeconds(5)); + listener.WaitForEnumerationComplete(s_waitForEventTimeout); events = listener.Events.ToArray(); } @@ -467,6 +480,7 @@ public void EventSourcePublishesInstruments() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesAllDataTypes() { using Meter meter = new Meter("TestMeter12"); @@ -479,9 +493,10 @@ public void EventSourcePublishesAllDataTypes() Counter d = meter.CreateCounter("counterDouble"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter12")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter12")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + i.Add(1_234_567); s.Add(21_432); b.Add(1); @@ -497,7 +512,7 @@ public void EventSourcePublishesAllDataTypes() dec.Add(1); f.Add(1); d.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); i.Add(1_234_567); s.Add(21_432); @@ -514,7 +529,7 @@ public void EventSourcePublishesAllDataTypes() dec.Add(1); f.Add(1); d.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -527,10 +542,11 @@ public void EventSourcePublishesAllDataTypes() AssertCounterEventsPresent(events, meter.Name, dec.Name, "", "", "123456789012346", "123456789012346"); AssertCounterEventsPresent(events, meter.Name, f.Name, "", "", "123457.7890625", "123457.7890625"); AssertCounterEventsPresent(events, meter.Name, d.Name, "", "", "87654321987655.4", "87654321987655.4"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceEnforcesTimeSeriesLimit() { using Meter meter = new Meter("TestMeter13"); @@ -538,20 +554,21 @@ public void EventSourceEnforcesTimeSeriesLimit() EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 2, 50, "TestMeter13")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, 2, 50, "TestMeter13")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5, new KeyValuePair("Color", "red")); c.Add(6, new KeyValuePair("Color", "blue")); c.Add(7, new KeyValuePair("Color", "green")); c.Add(8, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12, new KeyValuePair("Color", "red")); c.Add(13, new KeyValuePair("Color", "blue")); c.Add(14, new KeyValuePair("Color", "green")); c.Add(15, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -562,10 +579,11 @@ public void EventSourceEnforcesTimeSeriesLimit() AssertTimeSeriesLimitPresent(events); AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=green"); AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=yellow"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceEnforcesHistogramLimit() { using Meter meter = new Meter("TestMeter14"); @@ -573,20 +591,21 @@ public void EventSourceEnforcesHistogramLimit() EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 50, 2, "TestMeter14")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, 50, 2, "TestMeter14")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + h.Record(5, new KeyValuePair("Color", "red")); h.Record(6, new KeyValuePair("Color", "blue")); h.Record(7, new KeyValuePair("Color", "green")); h.Record(8, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); h.Record(12, new KeyValuePair("Color", "red")); h.Record(13, new KeyValuePair("Color", "blue")); h.Record(14, new KeyValuePair("Color", "green")); h.Record(15, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -597,7 +616,7 @@ public void EventSourceEnforcesHistogramLimit() AssertHistogramLimitPresent(events); AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=green"); AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=yellow"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } private void AssertBeginInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) @@ -746,8 +765,8 @@ private void AssertGaugeEventsPresent(EventWrittenEventArgs[] events, string met Assert.True(filteredEvents.Length >= expectedValues.Length); for (int i = 0; i < expectedValues.Length; i++) { - Assert.Equal(filteredEvents[i].Unit, expectedUnit); - Assert.Equal(filteredEvents[i].Value, expectedValues[i]); + Assert.Equal(expectedUnit, filteredEvents[i].Unit); + Assert.Equal(expectedValues[i], filteredEvents[i].Value); } } @@ -906,7 +925,8 @@ protected override void OnEventSourceCreated(EventSource eventSource) protected override void OnEventWritten(EventWrittenEventArgs eventData) { - if(eventData.EventName != "Message" && eventData.EventName != "Error" && eventData.Payload[0].ToString() != _sessionId) + string sessionId = eventData.Payload[0].ToString(); + if(sessionId != "" && sessionId != _sessionId) { return; } From 83a4d3cc02fb04fce17b24fc09b3cdf77a12ba51 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Sat, 10 Jul 2021 21:04:54 -0700 Subject: [PATCH 68/72] Relax SystemTrustCertificateWithCustomRootTrust test --- .../tests/ChainTests.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 8e9db6ed69585..135eed056df9d 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -245,6 +245,7 @@ public static void BuildChainExtraStoreUntrustedRoot() public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificateToCustomRootTrust) { using (var microsoftDotCom = new X509Certificate2(TestData.MicrosoftDotComSslCertBytes)) + using (var microsoftDotComIssuer = new X509Certificate2(TestData.MicrosoftDotComIssuerBytes)) using (var testCert = new X509Certificate2(TestFiles.ChainPfxFile, TestData.ChainPfxPassword)) using (var chainHolder = new ChainHolder()) { @@ -252,6 +253,7 @@ public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificate chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationTime = microsoftDotCom.NotBefore.AddSeconds(1); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.ExtraStore.Add(microsoftDotComIssuer); if (addCertificateToCustomRootTrust) { @@ -269,16 +271,29 @@ public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificate { Assert.False(chain.Build(microsoftDotCom)); - // Linux and Windows do not search the default system root stores when CustomRootTrust is enabled + // Historically, Windows has not searched system stores when CustomRootTrust is enabled. + // That seems to have recently (as of 2021-07-09) changed. + + Assert.InRange(chain.ChainElements.Count, 2, 3); + + if (chain.ChainElements.Count < 3) + { + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + } + else + { + Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + } + + // Check some known conditions. + if (PlatformDetection.UsesAppleCrypto) { Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); } - else + else if (OperatingSystem.IsLinux()) { Assert.Equal(2, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } } } From 6a47ecf4a8ee670356f5e554f08afb0b32cdac9a Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sun, 11 Jul 2021 16:46:53 +0200 Subject: [PATCH 69/72] W^X support (#54954) * W^X support This change is the last part of enabling the W^X support. It adds the actual executable allocator that handles all double mapped memory allocations and creating the writeable mappings. The platform specific functionality is placed in a new minipal that is going to be a basis for future removal of Windows APIs usage from the native runtime. The last state of the change was tested on all the platforms that we support using coreclr pri 1 tests with both the W^X enabled and disabled using the COMPlus_EnableWriteXorExecute variable. The debugger changes were tested using the managed debugger testing suite on Windows x64, x86 and on Apple Silicon so far. Further testing on other platforms is in progress. * Replace LeafLock in UMEntryThunkFreeList by a new lock * Also allocate LoaderHeapFreeBlock from regular heap. * Set the W^X default to disabled --- src/coreclr/CMakeLists.txt | 3 + src/coreclr/clrdefinitions.cmake | 4 - src/coreclr/debug/ee/arm64/arm64walker.cpp | 9 +- src/coreclr/debug/ee/controller.cpp | 47 +- src/coreclr/debug/ee/controller.h | 18 +- src/coreclr/debug/ee/debugger.cpp | 30 +- src/coreclr/debug/ee/debugger.h | 32 +- src/coreclr/debug/inc/amd64/primitives.h | 22 +- src/coreclr/debug/inc/arm/primitives.h | 13 +- src/coreclr/debug/inc/arm64/primitives.h | 6 +- src/coreclr/debug/inc/i386/primitives.h | 13 +- .../dlls/mscoree/coreclr/CMakeLists.txt | 1 + src/coreclr/inc/CrstTypes.def | 7 + src/coreclr/inc/clrconfigvalues.h | 4 + src/coreclr/inc/crsttypes.h | 218 ++--- src/coreclr/inc/executableallocator.h | 201 ++++- src/coreclr/inc/jithelpers.h | 12 +- src/coreclr/inc/utilcode.h | 29 - src/coreclr/minipal/CMakeLists.txt | 7 + src/coreclr/minipal/Unix/CMakeLists.txt | 4 + src/coreclr/minipal/Unix/doublemapping.cpp | 211 +++++ src/coreclr/minipal/Windows/CMakeLists.txt | 4 + src/coreclr/minipal/Windows/doublemapping.cpp | 205 +++++ src/coreclr/minipal/minipal.h | 78 ++ src/coreclr/utilcode/CMakeLists.txt | 1 + src/coreclr/utilcode/executableallocator.cpp | 755 ++++++++++++++++++ src/coreclr/utilcode/loaderheap.cpp | 119 +-- src/coreclr/utilcode/util.cpp | 162 ---- src/coreclr/vm/CMakeLists.txt | 4 +- src/coreclr/vm/amd64/JitHelpers_Fast.asm | 79 +- src/coreclr/vm/amd64/jithelpers_fast.S | 26 +- src/coreclr/vm/amd64/jitinterfaceamd64.cpp | 20 +- src/coreclr/vm/arm/armsinglestepper.cpp | 30 +- src/coreclr/vm/arm/asmhelpers.S | 10 + src/coreclr/vm/arm/asmhelpers.asm | 12 + src/coreclr/vm/arm/cgencpu.h | 13 + src/coreclr/vm/arm/stubs.cpp | 23 +- src/coreclr/vm/arm64/arm64singlestepper.cpp | 12 +- src/coreclr/vm/arm64/asmhelpers.S | 10 - src/coreclr/vm/arm64/asmhelpers.asm | 35 +- src/coreclr/vm/arm64/cgencpu.h | 13 + src/coreclr/vm/arm64/stubs.cpp | 10 +- src/coreclr/vm/ceemain.cpp | 9 +- src/coreclr/vm/class.cpp | 5 +- src/coreclr/vm/codeman.cpp | 27 +- src/coreclr/vm/comcallablewrapper.cpp | 18 +- src/coreclr/vm/comcallablewrapper.h | 4 + src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/dllimportcallback.cpp | 2 +- src/coreclr/vm/dynamicmethod.cpp | 7 +- src/coreclr/vm/excep.cpp | 2 - src/coreclr/vm/exceptionhandling.cpp | 6 - src/coreclr/vm/gccover.cpp | 4 +- src/coreclr/vm/i386/jithelp.S | 30 +- src/coreclr/vm/i386/jithelp.asm | 35 +- src/coreclr/vm/i386/jitinterfacex86.cpp | 84 +- src/coreclr/vm/i386/stublinkerx86.cpp | 2 +- src/coreclr/vm/i386/stublinkerx86.h | 10 +- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/jitinterface.h | 42 +- src/coreclr/vm/loaderallocator.cpp | 17 +- src/coreclr/vm/loaderallocator.inl | 6 - src/coreclr/vm/method.cpp | 40 - src/coreclr/vm/precode.cpp | 4 +- src/coreclr/vm/stackwalk.cpp | 2 - src/coreclr/vm/stublink.cpp | 14 +- src/coreclr/vm/stublink.h | 2 +- src/coreclr/vm/threads.cpp | 123 ++- src/coreclr/vm/threads.h | 19 +- src/coreclr/vm/virtualcallstub.cpp | 14 +- 70 files changed, 2258 insertions(+), 786 deletions(-) create mode 100644 src/coreclr/minipal/CMakeLists.txt create mode 100644 src/coreclr/minipal/Unix/CMakeLists.txt create mode 100644 src/coreclr/minipal/Unix/doublemapping.cpp create mode 100644 src/coreclr/minipal/Windows/CMakeLists.txt create mode 100644 src/coreclr/minipal/Windows/doublemapping.cpp create mode 100644 src/coreclr/minipal/minipal.h create mode 100644 src/coreclr/utilcode/executableallocator.cpp diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 78aa969473525..b4a4859342702 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -119,6 +119,8 @@ add_subdirectory(pal/prebuilt/inc) add_subdirectory(debug/debug-pal) +add_subdirectory(minipal) + if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(gc/sample) endif() @@ -171,6 +173,7 @@ include_directories("classlibnative/cryptography") include_directories("classlibnative/inc") include_directories("${GENERATED_INCLUDE_DIR}") include_directories("hosts/inc") +include_directories("minipal") if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) include_directories("${GENERATED_INCLUDE_DIR}/etw") diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index eeb421cac4c2f..0485ff99a99eb 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -224,10 +224,6 @@ if(CLR_CMAKE_TARGET_WIN32) endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) endif(CLR_CMAKE_TARGET_WIN32) -if(CLR_CMAKE_TARGET_OSX) - add_definitions(-DFEATURE_WRITEBARRIER_COPY) -endif(CLR_CMAKE_TARGET_OSX) - if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) add_compile_definitions($<$>>:FEATURE_EH_FUNCLETS>) endif (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/debug/ee/arm64/arm64walker.cpp b/src/coreclr/debug/ee/arm64/arm64walker.cpp index ae6e8c1fc2933..6c4dee9349700 100644 --- a/src/coreclr/debug/ee/arm64/arm64walker.cpp +++ b/src/coreclr/debug/ee/arm64/arm64walker.cpp @@ -171,7 +171,14 @@ BYTE* NativeWalker::SetupOrSimulateInstructionForPatchSkip(T_CONTEXT * context, { CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, 0xd503201f); //Add Nop in buffer - m_pSharedPatchBypassBuffer->RipTargetFixup = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder ripTargetFixupWriterHolder(&m_pSharedPatchBypassBuffer->RipTargetFixup, sizeof(UINT_PTR)); + UINT_PTR *pRipTargetFixupRW = ripTargetFixupWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + UINT_PTR *pRipTargetFixupRW = &m_pSharedPatchBypassBuffer->RipTargetFixup; +#endif // HOST_OSX && HOST_ARM64 + + *pRipTargetFixupRW = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook LOG((LF_CORDB, LL_INFO100000, "Arm64Walker::Simulate opcode: %x is a Control Flow instr \n", opcode)); if (walk == WALK_CALL) //initialize Lr diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index b17ae8f115002..f9304d16ab070 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -84,8 +84,13 @@ SharedPatchBypassBuffer* DebuggerControllerPatch::GetOrCreateSharedPatchBypassBu if (m_pSharedPatchBypassBuffer == NULL) { void *pSharedPatchBypassBufferRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(SharedPatchBypassBuffer)); +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX, sizeof(SharedPatchBypassBuffer)); - new (sharedPatchBypassBufferWriterHolder.GetRW()) SharedPatchBypassBuffer(); + void *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + void *pSharedPatchBypassBufferRW = pSharedPatchBypassBufferRX; +#endif // HOST_OSX && HOST_ARM64 + new (pSharedPatchBypassBufferRW) SharedPatchBypassBuffer(); m_pSharedPatchBypassBuffer = (SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX; _ASSERTE(m_pSharedPatchBypassBuffer); @@ -4351,7 +4356,15 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // m_pSharedPatchBypassBuffer = patch->GetOrCreateSharedPatchBypassBuffer(); - BYTE* patchBypass = m_pSharedPatchBypassBuffer->PatchBypass; +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)m_pSharedPatchBypassBuffer, sizeof(SharedPatchBypassBuffer)); + SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = m_pSharedPatchBypassBuffer; +#endif // HOST_OSX && HOST_ARM64 + + BYTE* patchBypassRX = m_pSharedPatchBypassBuffer->PatchBypass; + BYTE* patchBypassRW = pSharedPatchBypassBufferRW->PatchBypass; LOG((LF_CORDB, LL_INFO10000, "DPS::DPS: Patch skip for opcode 0x%.4x at address %p buffer allocated at 0x%.8x\n", patch->opcode, patch->address, m_pSharedPatchBypassBuffer)); // Copy the instruction block over to the patch skip @@ -4367,19 +4380,19 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // the 2nd skip executes the new jump-stamp code and not the original method prologue code. Copying // the code every time ensures that we have the most up-to-date version of the code in the buffer. _ASSERTE( patch->IsBound() ); - CopyInstructionBlock(patchBypass, (const BYTE *)patch->address); + CopyInstructionBlock(patchBypassRW, (const BYTE *)patch->address); // Technically, we could create a patch skipper for an inactive patch, but we rely on the opcode being // set here. _ASSERTE( patch->IsActivated() ); - CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, patch->opcode); + CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypassRW, patch->opcode); LOG((LF_CORDB, LL_EVERYTHING, "SetInstruction was called\n")); // // Look at instruction to get some attributes // - NativeWalker::DecodeInstructionForPatchSkip(patchBypass, &(m_instrAttrib)); + NativeWalker::DecodeInstructionForPatchSkip(patchBypassRX, &(m_instrAttrib)); #if defined(TARGET_AMD64) @@ -4395,33 +4408,33 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // Populate the RIP-relative buffer with the current value if needed // - BYTE* bufferBypass = m_pSharedPatchBypassBuffer->BypassBuffer; + BYTE* bufferBypassRW = pSharedPatchBypassBufferRW->BypassBuffer; // Overwrite the *signed* displacement. - int dwOldDisp = *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]); + int dwOldDisp = *(int*)(&patchBypassRX[m_instrAttrib.m_dwOffsetToDisp]); int dwNewDisp = offsetof(SharedPatchBypassBuffer, BypassBuffer) - (offsetof(SharedPatchBypassBuffer, PatchBypass) + m_instrAttrib.m_cbInstr); - *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp; + *(int*)(&patchBypassRW[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp; // This could be an LEA, which we'll just have to change into a MOV // and copy the original address - if (((patchBypass[0] == 0x4C) || (patchBypass[0] == 0x48)) && (patchBypass[1] == 0x8d)) + if (((patchBypassRX[0] == 0x4C) || (patchBypassRX[0] == 0x48)) && (patchBypassRX[1] == 0x8d)) { - patchBypass[1] = 0x8b; // MOV reg, mem + patchBypassRW[1] = 0x8b; // MOV reg, mem _ASSERTE((int)sizeof(void*) <= SharedPatchBypassBuffer::cbBufferBypass); - *(void**)bufferBypass = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); + *(void**)bufferBypassRW = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); } else { _ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass); // Copy the data into our buffer. - memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); + memcpy(bufferBypassRW, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); if (m_instrAttrib.m_fIsWrite) { // save the actual destination address and size so when we TriggerSingleStep() we can update the value - m_pSharedPatchBypassBuffer->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); - m_pSharedPatchBypassBuffer->RipTargetFixupSize = m_instrAttrib.m_cOperandSize; + pSharedPatchBypassBufferRW->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); + pSharedPatchBypassBufferRW->RipTargetFixupSize = m_instrAttrib.m_cOperandSize; } } } @@ -4490,17 +4503,17 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, #else // FEATURE_EMULATE_SINGLESTEP #ifdef TARGET_ARM64 - patchBypass = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode); + patchBypassRX = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode); #endif //TARGET_ARM64 //set eip to point to buffer... - SetIP(context, (PCODE)patchBypass); + SetIP(context, (PCODE)patchBypassRX); if (context ==(T_CONTEXT*) &c) thread->SetThreadContext(&c); - LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypass, patch->opcode)); + LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypassRX, patch->opcode)); // // Turn on single step (if the platform supports it) so we can diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h index 12b1106f7a4b2..6996439c31fba 100644 --- a/src/coreclr/debug/ee/controller.h +++ b/src/coreclr/debug/ee/controller.h @@ -266,14 +266,28 @@ class SharedPatchBypassBuffer LONG AddRef() { - LONG newRefCount = InterlockedIncrement(&m_refCount); +#if !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG)); + LONG *pRefCountRW = refCountWriterHolder.GetRW(); +#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + LONG *pRefCountRW = &m_refCount; +#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + + LONG newRefCount = InterlockedIncrement(pRefCountRW); _ASSERTE(newRefCount > 0); return newRefCount; } LONG Release() { - LONG newRefCount = InterlockedDecrement(&m_refCount); +#if !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG)); + LONG *pRefCountRW = refCountWriterHolder.GetRW(); +#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + LONG *pRefCountRW = &m_refCount; +#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + + LONG newRefCount = InterlockedDecrement(pRefCountRW); _ASSERTE(newRefCount >= 0); if (newRefCount == 0) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 53ee5555ace43..e4563a31757f4 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -1317,13 +1317,19 @@ DebuggerEval::DebuggerEval(CONTEXT * pContext, DebuggerIPCE_FuncEvalInfo * pEval // Allocate the breakpoint instruction info in executable memory. void *bpInfoSegmentRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(DebuggerEvalBreakpointInfoSegment)); + +#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder bpInfoSegmentWriterHolder((DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX, sizeof(DebuggerEvalBreakpointInfoSegment)); - new (bpInfoSegmentWriterHolder.GetRW()) DebuggerEvalBreakpointInfoSegment(this); + DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = bpInfoSegmentWriterHolder.GetRW(); +#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX; +#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + new (bpInfoSegmentRW) DebuggerEvalBreakpointInfoSegment(this); m_bpInfoSegment = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX; // This must be non-zero so that the saved opcode is non-zero, and on IA64 we want it to be 0x16 // so that we can have a breakpoint instruction in any slot in the bundle. - bpInfoSegmentWriterHolder.GetRW()->m_breakpointInstruction[0] = 0x16; + bpInfoSegmentRW->m_breakpointInstruction[0] = 0x16; #if defined(TARGET_ARM) USHORT *bp = (USHORT*)&m_bpInfoSegment->m_breakpointInstruction; *bp = CORDbg_BREAK_INSTRUCTION; @@ -16234,6 +16240,7 @@ void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger) } #endif // DACCESS_COMPILE +#ifndef DACCESS_COMPILE /* ------------------------------------------------------------------------ * * Functions for DebuggerHeap executable memory allocations * ------------------------------------------------------------------------ */ @@ -16378,6 +16385,7 @@ void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(De return page->GetPointerToChunk(chunkNumber); } +#endif // DACCESS_COMPILE /* ------------------------------------------------------------------------ * * DebuggerHeap impl @@ -16412,7 +16420,7 @@ void DebuggerHeap::Destroy() m_hHeap = NULL; } #endif -#ifndef HOST_WINDOWS +#if !defined(HOST_WINDOWS) && !defined(DACCESS_COMPILE) if (m_execMemAllocator != NULL) { delete m_execMemAllocator; @@ -16439,6 +16447,8 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + // Have knob catch if we don't want to lazy init the debugger. _ASSERTE(!g_DbgShouldntUseDebugger); m_fExecutable = fExecutable; @@ -16472,7 +16482,9 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable) return E_OUTOFMEMORY; } } -#endif +#endif + +#endif // !DACCESS_COMPILE return S_OK; } @@ -16549,7 +16561,10 @@ void *DebuggerHeap::Alloc(DWORD size) size += sizeof(InteropHeapCanary); #endif - void *ret; + void *ret = NULL; + +#ifndef DACCESS_COMPILE + #ifdef USE_INTEROPSAFE_HEAP _ASSERTE(m_hHeap != NULL); ret = ::HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); @@ -16585,7 +16600,7 @@ void *DebuggerHeap::Alloc(DWORD size) InteropHeapCanary * pCanary = InteropHeapCanary::GetFromRawAddr(ret); ret = pCanary->GetUserAddr(); #endif - +#endif // !DACCESS_COMPILE return ret; } @@ -16638,6 +16653,8 @@ void DebuggerHeap::Free(void *pMem) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + #ifdef USE_INTEROPSAFE_CANARY // Check for canary @@ -16673,6 +16690,7 @@ void DebuggerHeap::Free(void *pMem) #endif // HOST_WINDOWS } #endif +#endif // !DACCESS_COMPILE } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index f16f8cd6d9d9d..5503de2459099 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -1054,6 +1054,8 @@ constexpr uint64_t CHUNKS_PER_DEBUGGERHEAP=(DEBUGGERHEAP_PAGESIZE / EXPECTED_CHU constexpr uint64_t MAX_CHUNK_MASK=((1ull << CHUNKS_PER_DEBUGGERHEAP) - 1); constexpr uint64_t BOOKKEEPING_CHUNK_MASK (1ull << (CHUNKS_PER_DEBUGGERHEAP - 1)); +#ifndef DACCESS_COMPILE + // Forward declaration struct DebuggerHeapExecutableMemoryPage; @@ -1110,8 +1112,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage inline void SetNextPage(DebuggerHeapExecutableMemoryPage* nextPage) { +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.nextPage = nextPage; + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif + pHeapPageRW->chunks[0].bookkeeping.nextPage = nextPage; } inline uint64_t GetPageOccupancy() const @@ -1124,8 +1131,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage // Can't unset the bookmark chunk! ASSERT((newOccupancy & BOOKKEEPING_CHUNK_MASK) != 0); ASSERT(newOccupancy <= MAX_CHUNK_MASK); +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.pageOccupancy = newOccupancy; + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif + pHeapPageRW->chunks[0].bookkeeping.pageOccupancy = newOccupancy; } inline void* GetPointerToChunk(int chunkNum) const @@ -1136,14 +1148,18 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage DebuggerHeapExecutableMemoryPage() { - ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - SetPageOccupancy(BOOKKEEPING_CHUNK_MASK); // only the first bit is set. +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif for (uint8_t i = 1; i < CHUNKS_PER_DEBUGGERHEAP; i++) { ASSERT(i != 0); - debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.startOfPage = this; - debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.chunkNumber = i; + pHeapPageRW->chunks[i].data.startOfPage = this; + pHeapPageRW->chunks[i].data.chunkNumber = i; } } @@ -1190,6 +1206,8 @@ class DebuggerHeapExecutableMemoryAllocator Crst m_execMemAllocMutex; }; +#endif // DACCESS_COMPILE + // ------------------------------------------------------------------------ * // DebuggerHeap class // For interop debugging, we need a heap that: @@ -1201,6 +1219,8 @@ class DebuggerHeapExecutableMemoryAllocator #define USE_INTEROPSAFE_HEAP #endif +class DebuggerHeapExecutableMemoryAllocator; + class DebuggerHeap { public: diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h index d8d14b24b5425..9d363938519c7 100644 --- a/src/coreclr/debug/inc/amd64/primitives.h +++ b/src/coreclr/debug/inc/amd64/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - #ifndef CORDB_ADDRESS_TYPE typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -191,14 +187,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address) { LIMITED_METHOD_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch) + *((unsigned char*)address) = 0xCC; // int 3 (single byte patch) FlushInstructionCache(GetCurrentProcess(), address, 1); } @@ -209,14 +198,7 @@ inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder instructionWriterHolder(address, sizeof(unsigned char)); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = + *((unsigned char*)address) = (unsigned char) instruction; // setting one byte is important FlushInstructionCache(GetCurrentProcess(), address, 1); diff --git a/src/coreclr/debug/inc/arm/primitives.h b/src/coreclr/debug/inc/arm/primitives.h index c4e2d28602e56..269281eb006be 100644 --- a/src/coreclr/debug/inc/arm/primitives.h +++ b/src/coreclr/debug/inc/arm/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - #ifndef THUMB_CODE #define THUMB_CODE 1 #endif @@ -163,14 +159,7 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder instructionWriterHolder(address, sizeof(PRD_TYPE)); - CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)addressRW; + CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)address; _ASSERTE(ptraddr & THUMB_CODE); ptraddr &= ~THUMB_CODE; diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h index 4f4c3f7bcd8f2..05c03c7b3094f 100644 --- a/src/coreclr/debug/inc/arm64/primitives.h +++ b/src/coreclr/debug/inc/arm64/primitives.h @@ -150,13 +150,13 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) +#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) ExecutableWriterHolder instructionWriterHolder((LPVOID)address, sizeof(PRD_TYPE)); ULONGLONG ptraddr = dac_cast(instructionWriterHolder.GetRW()); -#else // !DBI_COMPILE && !DACCESS_COMPILE +#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX ULONGLONG ptraddr = dac_cast(address); -#endif // !DBI_COMPILE && !DACCESS_COMPILE +#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX *(PRD_TYPE *)ptraddr = instruction; FlushInstructionCache(GetCurrentProcess(), address, diff --git a/src/coreclr/debug/inc/i386/primitives.h b/src/coreclr/debug/inc/i386/primitives.h index 313b42c5a1970..2f228b3a3a9a1 100644 --- a/src/coreclr/debug/inc/i386/primitives.h +++ b/src/coreclr/debug/inc/i386/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -151,14 +147,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address) { LIMITED_METHOD_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch) + *((unsigned char*)address) = 0xCC; // int 3 (single byte patch) FlushInstructionCache(GetCurrentProcess(), address, 1); } diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index fae55ecdc3ea5..9b8e4b649864d 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -109,6 +109,7 @@ set(CORECLR_LIBRARIES v3binder System.Globalization.Native-Static interop + coreclrminipal ) if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index c48872a0b9424..c7266df7dbb01 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -201,6 +201,10 @@ End Crst Exception End +Crst ExecutableAllocatorLock + AcquiredAfter LoaderHeap ArgBasedStubCache UMEntryThunkFreeListLock +End + Crst ExecuteManRangeLock End @@ -505,6 +509,9 @@ Crst TypeEquivalenceMap AcquiredBefore LoaderHeap End +Crst UMEntryThunkFreeListLock +End + Crst UniqueStack AcquiredBefore LoaderHeap End diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 0d2a1db98e471..e2f1a63a20fec 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -737,6 +737,10 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_DOTNET_DiagnosticPorts, W("DiagnosticPorts"), RETAIL_CONFIG_STRING_INFO(INTERNAL_LTTngConfig, W("LTTngConfig"), "Configuration for LTTng.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") +// +// Executable code +// +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 0, "Enable W^X for executable memory."); #ifdef FEATURE_GDBJIT /// diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h index a1bab2ecb906c..7be482c48bb55 100644 --- a/src/coreclr/inc/crsttypes.h +++ b/src/coreclr/inc/crsttypes.h @@ -49,92 +49,94 @@ enum CrstType CrstEventPipe = 31, CrstEventStore = 32, CrstException = 33, - CrstExecuteManRangeLock = 34, - CrstExternalObjectContextCache = 35, - CrstFCall = 36, - CrstFuncPtrStubs = 37, - CrstFusionAppCtx = 38, - CrstGCCover = 39, - CrstGlobalStrLiteralMap = 40, - CrstHandleTable = 41, - CrstHostAssemblyMap = 42, - CrstHostAssemblyMapAdd = 43, - CrstIbcProfile = 44, - CrstIJWFixupData = 45, - CrstIJWHash = 46, - CrstILStubGen = 47, - CrstInlineTrackingMap = 48, - CrstInstMethodHashTable = 49, - CrstInterop = 50, - CrstInteropData = 51, - CrstIsJMCMethod = 52, - CrstISymUnmanagedReader = 53, - CrstJit = 54, - CrstJitGenericHandleCache = 55, - CrstJitInlineTrackingMap = 56, - CrstJitPatchpoint = 57, - CrstJitPerf = 58, - CrstJumpStubCache = 59, - CrstLeafLock = 60, - CrstListLock = 61, - CrstLoaderAllocator = 62, - CrstLoaderAllocatorReferences = 63, - CrstLoaderHeap = 64, - CrstManagedObjectWrapperMap = 65, - CrstMethodDescBackpatchInfoTracker = 66, - CrstModule = 67, - CrstModuleFixup = 68, - CrstModuleLookupTable = 69, - CrstMulticoreJitHash = 70, - CrstMulticoreJitManager = 71, - CrstNativeImageEagerFixups = 72, - CrstNativeImageLoad = 73, - CrstNls = 74, - CrstNotifyGdb = 75, - CrstObjectList = 76, - CrstPEImage = 77, - CrstPendingTypeLoadEntry = 78, - CrstPgoData = 79, - CrstPinnedByrefValidation = 80, - CrstProfilerGCRefDataFreeList = 81, - CrstProfilingAPIStatus = 82, - CrstRCWCache = 83, - CrstRCWCleanupList = 84, - CrstReadyToRunEntryPointToMethodDescMap = 85, - CrstReflection = 86, - CrstReJITGlobalRequest = 87, - CrstRetThunkCache = 88, - CrstSavedExceptionInfo = 89, - CrstSaveModuleProfileData = 90, - CrstSecurityStackwalkCache = 91, - CrstSigConvert = 92, - CrstSingleUseLock = 93, - CrstSpecialStatics = 94, - CrstStackSampler = 95, - CrstStressLog = 96, - CrstStubCache = 97, - CrstStubDispatchCache = 98, - CrstStubUnwindInfoHeapSegments = 99, - CrstSyncBlockCache = 100, - CrstSyncHashLock = 101, - CrstSystemBaseDomain = 102, - CrstSystemDomain = 103, - CrstSystemDomainDelayedUnloadList = 104, - CrstThreadIdDispenser = 105, - CrstThreadpoolTimerQueue = 106, - CrstThreadpoolWaitThreads = 107, - CrstThreadpoolWorker = 108, - CrstThreadStore = 109, - CrstTieredCompilation = 110, - CrstTypeEquivalenceMap = 111, - CrstTypeIDMap = 112, - CrstUMEntryThunkCache = 113, - CrstUniqueStack = 114, - CrstUnresolvedClassLock = 115, - CrstUnwindInfoTableLock = 116, - CrstVSDIndirectionCellLock = 117, - CrstWrapperTemplate = 118, - kNumberOfCrstTypes = 119 + CrstExecutableAllocatorLock = 34, + CrstExecuteManRangeLock = 35, + CrstExternalObjectContextCache = 36, + CrstFCall = 37, + CrstFuncPtrStubs = 38, + CrstFusionAppCtx = 39, + CrstGCCover = 40, + CrstGlobalStrLiteralMap = 41, + CrstHandleTable = 42, + CrstHostAssemblyMap = 43, + CrstHostAssemblyMapAdd = 44, + CrstIbcProfile = 45, + CrstIJWFixupData = 46, + CrstIJWHash = 47, + CrstILStubGen = 48, + CrstInlineTrackingMap = 49, + CrstInstMethodHashTable = 50, + CrstInterop = 51, + CrstInteropData = 52, + CrstIsJMCMethod = 53, + CrstISymUnmanagedReader = 54, + CrstJit = 55, + CrstJitGenericHandleCache = 56, + CrstJitInlineTrackingMap = 57, + CrstJitPatchpoint = 58, + CrstJitPerf = 59, + CrstJumpStubCache = 60, + CrstLeafLock = 61, + CrstListLock = 62, + CrstLoaderAllocator = 63, + CrstLoaderAllocatorReferences = 64, + CrstLoaderHeap = 65, + CrstManagedObjectWrapperMap = 66, + CrstMethodDescBackpatchInfoTracker = 67, + CrstModule = 68, + CrstModuleFixup = 69, + CrstModuleLookupTable = 70, + CrstMulticoreJitHash = 71, + CrstMulticoreJitManager = 72, + CrstNativeImageEagerFixups = 73, + CrstNativeImageLoad = 74, + CrstNls = 75, + CrstNotifyGdb = 76, + CrstObjectList = 77, + CrstPEImage = 78, + CrstPendingTypeLoadEntry = 79, + CrstPgoData = 80, + CrstPinnedByrefValidation = 81, + CrstProfilerGCRefDataFreeList = 82, + CrstProfilingAPIStatus = 83, + CrstRCWCache = 84, + CrstRCWCleanupList = 85, + CrstReadyToRunEntryPointToMethodDescMap = 86, + CrstReflection = 87, + CrstReJITGlobalRequest = 88, + CrstRetThunkCache = 89, + CrstSavedExceptionInfo = 90, + CrstSaveModuleProfileData = 91, + CrstSecurityStackwalkCache = 92, + CrstSigConvert = 93, + CrstSingleUseLock = 94, + CrstSpecialStatics = 95, + CrstStackSampler = 96, + CrstStressLog = 97, + CrstStubCache = 98, + CrstStubDispatchCache = 99, + CrstStubUnwindInfoHeapSegments = 100, + CrstSyncBlockCache = 101, + CrstSyncHashLock = 102, + CrstSystemBaseDomain = 103, + CrstSystemDomain = 104, + CrstSystemDomainDelayedUnloadList = 105, + CrstThreadIdDispenser = 106, + CrstThreadpoolTimerQueue = 107, + CrstThreadpoolWaitThreads = 108, + CrstThreadpoolWorker = 109, + CrstThreadStore = 110, + CrstTieredCompilation = 111, + CrstTypeEquivalenceMap = 112, + CrstTypeIDMap = 113, + CrstUMEntryThunkCache = 114, + CrstUMEntryThunkFreeListLock = 115, + CrstUniqueStack = 116, + CrstUnresolvedClassLock = 117, + CrstUnwindInfoTableLock = 118, + CrstVSDIndirectionCellLock = 119, + CrstWrapperTemplate = 120, + kNumberOfCrstTypes = 121 }; #endif // __CRST_TYPES_INCLUDED @@ -147,11 +149,11 @@ int g_rgCrstLevelMap[] = { 10, // CrstAppDomainCache 14, // CrstAppDomainHandleTable - 0, // CrstArgBasedStubCache + 3, // CrstArgBasedStubCache 0, // CrstAssemblyList 12, // CrstAssemblyLoader - 3, // CrstAvailableClass - 4, // CrstAvailableParamTypes + 4, // CrstAvailableClass + 5, // CrstAvailableParamTypes 7, // CrstBaseDomain -1, // CrstCCompRC 13, // CrstClassFactInfoHash @@ -160,7 +162,7 @@ int g_rgCrstLevelMap[] = 6, // CrstCodeFragmentHeap 9, // CrstCodeVersioning 0, // CrstCOMCallWrapper - 4, // CrstCOMWrapperCache + 5, // CrstCOMWrapperCache 3, // CrstDataTest1 0, // CrstDataTest2 0, // CrstDbgTransport @@ -179,9 +181,10 @@ int g_rgCrstLevelMap[] = 18, // CrstEventPipe 0, // CrstEventStore 0, // CrstException + 0, // CrstExecutableAllocatorLock 0, // CrstExecuteManRangeLock 0, // CrstExternalObjectContextCache - 3, // CrstFCall + 4, // CrstFCall 7, // CrstFuncPtrStubs 10, // CrstFusionAppCtx 10, // CrstGCCover @@ -196,25 +199,25 @@ int g_rgCrstLevelMap[] = 3, // CrstInlineTrackingMap 17, // CrstInstMethodHashTable 20, // CrstInterop - 4, // CrstInteropData + 5, // CrstInteropData 0, // CrstIsJMCMethod 7, // CrstISymUnmanagedReader 11, // CrstJit 0, // CrstJitGenericHandleCache 16, // CrstJitInlineTrackingMap - 3, // CrstJitPatchpoint + 4, // CrstJitPatchpoint -1, // CrstJitPerf 6, // CrstJumpStubCache 0, // CrstLeafLock -1, // CrstListLock 15, // CrstLoaderAllocator 16, // CrstLoaderAllocatorReferences - 0, // CrstLoaderHeap + 3, // CrstLoaderHeap 3, // CrstManagedObjectWrapperMap 14, // CrstMethodDescBackpatchInfoTracker - 4, // CrstModule + 5, // CrstModule 15, // CrstModuleFixup - 3, // CrstModuleLookupTable + 4, // CrstModuleLookupTable 0, // CrstMulticoreJitHash 13, // CrstMulticoreJitManager 0, // CrstNativeImageEagerFixups @@ -222,22 +225,22 @@ int g_rgCrstLevelMap[] = 0, // CrstNls 0, // CrstNotifyGdb 2, // CrstObjectList - 4, // CrstPEImage + 5, // CrstPEImage 19, // CrstPendingTypeLoadEntry - 3, // CrstPgoData + 4, // CrstPgoData 0, // CrstPinnedByrefValidation 0, // CrstProfilerGCRefDataFreeList 0, // CrstProfilingAPIStatus - 3, // CrstRCWCache + 4, // CrstRCWCache 0, // CrstRCWCleanupList 10, // CrstReadyToRunEntryPointToMethodDescMap 8, // CrstReflection 17, // CrstReJITGlobalRequest - 3, // CrstRetThunkCache + 4, // CrstRetThunkCache 3, // CrstSavedExceptionInfo 0, // CrstSaveModuleProfileData 0, // CrstSecurityStackwalkCache - 3, // CrstSigConvert + 4, // CrstSigConvert 5, // CrstSingleUseLock 0, // CrstSpecialStatics 0, // CrstStackSampler @@ -247,7 +250,7 @@ int g_rgCrstLevelMap[] = 4, // CrstStubUnwindInfoHeapSegments 3, // CrstSyncBlockCache 0, // CrstSyncHashLock - 4, // CrstSystemBaseDomain + 5, // CrstSystemBaseDomain 13, // CrstSystemDomain 0, // CrstSystemDomainDelayedUnloadList 0, // CrstThreadIdDispenser @@ -256,13 +259,14 @@ int g_rgCrstLevelMap[] = 13, // CrstThreadpoolWorker 12, // CrstThreadStore 8, // CrstTieredCompilation - 3, // CrstTypeEquivalenceMap + 4, // CrstTypeEquivalenceMap 10, // CrstTypeIDMap - 3, // CrstUMEntryThunkCache - 3, // CrstUniqueStack + 4, // CrstUMEntryThunkCache + 3, // CrstUMEntryThunkFreeListLock + 4, // CrstUniqueStack 7, // CrstUnresolvedClassLock 3, // CrstUnwindInfoTableLock - 3, // CrstVSDIndirectionCellLock + 4, // CrstVSDIndirectionCellLock 3, // CrstWrapperTemplate }; @@ -303,6 +307,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstEventPipe", "CrstEventStore", "CrstException", + "CrstExecutableAllocatorLock", "CrstExecuteManRangeLock", "CrstExternalObjectContextCache", "CrstFCall", @@ -383,6 +388,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstTypeEquivalenceMap", "CrstTypeIDMap", "CrstUMEntryThunkCache", + "CrstUMEntryThunkFreeListLock", "CrstUniqueStack", "CrstUnresolvedClassLock", "CrstUnwindInfoTableLock", diff --git a/src/coreclr/inc/executableallocator.h b/src/coreclr/inc/executableallocator.h index ce0c6c22f890e..101178f9a4ef0 100644 --- a/src/coreclr/inc/executableallocator.h +++ b/src/coreclr/inc/executableallocator.h @@ -11,6 +11,191 @@ #include "utilcode.h" #include "ex.h" +#include "minipal.h" + +#ifndef DACCESS_COMPILE + +// This class is responsible for allocation of all the executable memory in the runtime. +class ExecutableAllocator +{ + // RX address range block descriptor + struct BlockRX + { + // Next block in a linked list + BlockRX* next; + // Base address of the block + void* baseRX; + // Size of the block + size_t size; + // Offset of the block in the shared memory + size_t offset; + }; + + // RW address range block descriptor + struct BlockRW + { + // Next block in a linked list + BlockRW* next; + // Base address of the RW mapping of the block + void* baseRW; + // Base address of the RX mapping of the block + void* baseRX; + // Size of the block + size_t size; + // Usage reference count of the RW block. RW blocks can be reused + // when multiple mappings overlap in the VA space at the same time + // (even from multiple threads) + size_t refCount; + }; + + typedef void (*FatalErrorHandler)(UINT errorCode, LPCWSTR pszMessage); + + // Instance of the allocator + static ExecutableAllocator* g_instance; + + // Callback to the runtime to report fatal errors + static FatalErrorHandler g_fatalErrorHandler; + +#if USE_UPPER_ADDRESS + // Preferred region to allocate the code in. + static BYTE* g_codeMinAddr; + static BYTE* g_codeMaxAddr; + static BYTE* g_codeAllocStart; + // Next address to try to allocate for code in the preferred region. + static BYTE* g_codeAllocHint; +#endif // USE_UPPER_ADDRESS + + // Caches the COMPlus_EnableWXORX setting + static bool g_isWXorXEnabled; + + // Head of the linked list of all RX blocks that were allocated by this allocator + BlockRX* m_pFirstBlockRX = NULL; + + // Head of the linked list of free RX blocks that were allocated by this allocator and then backed out + BlockRX* m_pFirstFreeBlockRX = NULL; + + // Head of the linked list of currently mapped RW blocks + BlockRW* m_pFirstBlockRW = NULL; + + // Handle of the double mapped memory mapper + void *m_doubleMemoryMapperHandle = NULL; + + // Maximum size of executable memory this allocator can allocate + size_t m_maxExecutableCodeSize; + + // First free offset in the underlying shared memory. It is not used + // for platforms that don't use shared memory. + size_t m_freeOffset = 0; + + // Last RW mapping cached so that it can be reused for the next mapping + // request if it goes into the same range. + BlockRW* m_cachedMapping = NULL; + + // Synchronization of the public allocator methods + CRITSEC_COOKIE m_CriticalSection; + + // Update currently cached mapping. If the passed in block is the same as the one + // in the cache, it keeps it cached. Otherwise it destroys the currently cached one + // and replaces it by the passed in one. + void UpdateCachedMapping(BlockRW *pBlock); + + // Find existing RW block that maps the whole specified range of RX memory. + // Return NULL if no such block exists. + void* FindRWBlock(void* baseRX, size_t size); + + // Add RW block to the list of existing RW blocks + bool AddRWBlock(void* baseRW, void* baseRX, size_t size); + + // Remove RW block from the list of existing RW blocks and return the base + // address and size the underlying memory was mapped at. + // Return false if no existing RW block contains the passed in address. + bool RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize); + + // Find a free block with the closest size >= the requested size. + // Returns NULL if no such block exists. + BlockRX* FindBestFreeBlock(size_t size); + + // Return memory mapping granularity. + static size_t Granularity(); + + // Allocate a block of executable memory of the specified size. + // It doesn't acquire the actual virtual memory, just the + // range of the underlying shared memory. + BlockRX* AllocateBlock(size_t size, bool* pIsFreeBlock); + + // Backout the block allocated by AllocateBlock in case of an + // error. + void BackoutBlock(BlockRX* pBlock, bool isFreeBlock); + + // Allocate range of offsets in the underlying shared memory + bool AllocateOffset(size_t* pOffset, size_t size); + + // Add RX block to the linked list of existing blocks + void AddRXBlock(BlockRX *pBlock); + + // Return true if double mapping is enabled. + static bool IsDoubleMappingEnabled(); + + // Initialize the allocator instance + bool Initialize(); + +public: + + // Return the ExecuteAllocator singleton instance + static ExecutableAllocator* Instance(); + + // Initialize the static members of the Executable allocator and allocate + // and initialize the instance of it. + static HRESULT StaticInitialize(FatalErrorHandler fatalErrorHandler); + + // Destroy the allocator + ~ExecutableAllocator(); + + // Return true if W^X is enabled + static bool IsWXORXEnabled(); + + // Use this function to initialize the g_codeAllocHint + // during startup. base is runtime .dll base address, + // size is runtime .dll virtual size. + static void InitCodeAllocHint(size_t base, size_t size, int randomPageOffset); + + // Use this function to reset the g_codeAllocHint + // after unloading an AppDomain + static void ResetCodeAllocHint(); + + // Returns TRUE if p is located in near clr.dll that allows us + // to use rel32 IP-relative addressing modes. + static bool IsPreferredExecutableRange(void* p); + + // Reserve the specified amount of virtual address space for executable mapping. + void* Reserve(size_t size); + + // Reserve the specified amount of virtual address space for executable mapping. + // The reserved range must be within the loAddress and hiAddress. If it is not + // possible to reserve memory in such range, the method returns NULL. + void* ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress); + + // Reserve the specified amount of virtual address space for executable mapping + // exactly at the given address. + void* ReserveAt(void* baseAddressRX, size_t size); + + // Commit the specified range of memory. The memory can be committed as executable (RX) + // or non-executable (RW) based on the passed in isExecutable flag. The non-executable + // allocations are used to allocate data structures that need to be close to the + // executable code due to memory addressing performance related reasons. + void* Commit(void* pStart, size_t size, bool isExecutable); + + // Release the executable memory block starting at the passed in address that was allocated + // by one of the ReserveXXX methods. + void Release(void* pRX); + + // Map the specified block of executable memory as RW + void* MapRW(void* pRX, size_t size); + + // Unmap the RW mapping at the specified address + void UnmapRW(void* pRW); +}; + // Holder class to map read-execute memory as read-write so that it can be modified without using read-write-execute mapping. // At the moment the implementation is dummy, returning the same addresses for both cases and expecting them to be read-write-execute. // The class uses the move semantics to ensure proper unmapping in case of re-assigning of the holder value. @@ -30,13 +215,17 @@ class ExecutableWriterHolder void Unmap() { +#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) if (m_addressRX != NULL) { - // TODO: mapping / unmapping for targets using double memory mapping will be added with the double mapped allocator addition -#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) PAL_JitWriteProtect(false); -#endif } +#else + if (m_addressRX != m_addressRW) + { + ExecutableAllocator::Instance()->UnmapRW((void*)m_addressRW); + } +#endif } public: @@ -62,9 +251,11 @@ class ExecutableWriterHolder ExecutableWriterHolder(T* addressRX, size_t size) { m_addressRX = addressRX; +#if defined(HOST_OSX) && defined(HOST_ARM64) m_addressRW = addressRX; -#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) PAL_JitWriteProtect(true); +#else + m_addressRW = (T *)ExecutableAllocator::Instance()->MapRW((void*)addressRX, size); #endif } @@ -79,3 +270,5 @@ class ExecutableWriterHolder return m_addressRW; } }; + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index fb65ea9fa613c..3c42f0850850b 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -302,12 +302,12 @@ #endif // !FEATURE_EH_FUNCLETS #ifdef TARGET_X86 - JITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EAX, JIT_CheckedWriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EBX, JIT_CheckedWriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index a47034ee2e05c..77df9dfa94d2a 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -1014,35 +1014,6 @@ void SplitPath(__in SString const &path, #define CLRGetTickCount64() GetTickCount64() -// -// Use this function to initialize the s_CodeAllocHint -// during startup. base is runtime .dll base address, -// size is runtime .dll virtual size. -// -void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset); - - -// -// Use this function to reset the s_CodeAllocHint -// after unloading an AppDomain -// -void ResetCodeAllocHint(); - -// -// Returns TRUE if p is located in near clr.dll that allows us -// to use rel32 IP-relative addressing modes. -// -BOOL IsPreferredExecutableRange(void * p); - -// -// Allocate free memory that will be used for executable code -// Handles the special requirements that we have on 64-bit platforms -// where we want the executable memory to be located near mscorwks -// -BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize, - DWORD flAllocationType, - DWORD flProtect); - // // Allocate free memory within the range [pMinAddr..pMaxAddr] using // ClrVirtualQuery to find free memory and ClrVirtualAlloc to allocate it. diff --git a/src/coreclr/minipal/CMakeLists.txt b/src/coreclr/minipal/CMakeLists.txt new file mode 100644 index 0000000000000..3096237d2a2fe --- /dev/null +++ b/src/coreclr/minipal/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(.) +if (CLR_CMAKE_HOST_UNIX) + add_subdirectory(Unix) +else (CLR_CMAKE_HOST_UNIX) + add_subdirectory(Windows) +endif (CLR_CMAKE_HOST_UNIX) + diff --git a/src/coreclr/minipal/Unix/CMakeLists.txt b/src/coreclr/minipal/Unix/CMakeLists.txt new file mode 100644 index 0000000000000..b56b5017d375f --- /dev/null +++ b/src/coreclr/minipal/Unix/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(coreclrminipal + STATIC + doublemapping.cpp +) diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp new file mode 100644 index 0000000000000..a50b326861aad --- /dev/null +++ b/src/coreclr/minipal/Unix/doublemapping.cpp @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef TARGET_LINUX +#include +#include // __NR_memfd_create +#endif // TARGET_LINUX +#include "minipal.h" + +#if defined(TARGET_OSX) && defined(TARGET_AMD64) +#include +#endif // TARGET_OSX && TARGET_AMD64 + +#ifndef TARGET_OSX + +#ifdef TARGET_64BIT +static const off_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024; +#else +static const off_t MaxDoubleMappedSize = UINT_MAX; +#endif + +#ifdef TARGET_LINUX +#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__) +#endif // TARGET_LINUX + +#endif // TARGET_OSX + +bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize) +{ +#ifndef TARGET_OSX + +#ifdef TARGET_FREEBSD + int fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, S_IRWXU); +#else // TARGET_FREEBSD + int fd = memfd_create("doublemapper", MFD_CLOEXEC); +#endif // TARGET_FREEBSD + + if (fd == -1) + { + return false; + } + + if (ftruncate(fd, MaxDoubleMappedSize) == -1) + { + close(fd); + return false; + } + + *pMaxExecutableCodeSize = MaxDoubleMappedSize; + *pHandle = (void*)(size_t)fd; +#else // !TARGET_OSX + *pMaxExecutableCodeSize = SIZE_MAX; + *pHandle = NULL; +#endif // !TARGET_OSX + + return true; +} + +void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle) +{ +#ifndef TARGET_OSX + close((int)(size_t)mapperHandle); +#endif +} + +extern "C" void* PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(const void* lpBeginAddress, const void* lpEndAddress, size_t dwSize); + +#ifdef TARGET_OSX +bool IsMapJitFlagNeeded() +{ + static volatile int isMapJitFlagNeeded = -1; + + if (isMapJitFlagNeeded == -1) + { + int mapJitFlagCheckResult = 0; + int pageSize = sysconf(_SC_PAGE_SIZE); + // Try to map a page with read-write-execute protection. It should fail on Mojave hardened runtime and higher. + void* testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (testPage == MAP_FAILED && (errno == EACCES)) + { + // The mapping has failed with EACCES, check if making the same mapping with MAP_JIT flag works + testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_JIT, -1, 0); + if (testPage != MAP_FAILED) + { + mapJitFlagCheckResult = 1; + } + } + + if (testPage != MAP_FAILED) + { + munmap(testPage, pageSize); + } + + isMapJitFlagNeeded = mapJitFlagCheckResult; + } + + return (bool)isMapJitFlagNeeded; +} +#endif // TARGET_OSX + +void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd) +{ + int fd = (int)(size_t)mapperHandle; + + if (rangeStart != NULL || rangeEnd != NULL) + { + void* result = PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(rangeStart, rangeEnd, size); +#ifndef TARGET_OSX + if (result != NULL) + { + // Map the shared memory over the range reserved from the executable memory allocator. + result = mmap(result, size, PROT_NONE, MAP_SHARED | MAP_FIXED, fd, offset); + if (result == MAP_FAILED) + { + assert(false); + result = NULL; + } + } +#endif // TARGET_OSX + + return result; + } + +#ifndef TARGET_OSX + void* result = mmap(NULL, size, PROT_NONE, MAP_SHARED, fd, offset); +#else + int mmapFlags = MAP_ANON | MAP_PRIVATE; + if (IsMapJitFlagNeeded()) + { + mmapFlags |= MAP_JIT; + } + void* result = mmap(NULL, size, PROT_NONE, mmapFlags, -1, 0); +#endif + if (result == MAP_FAILED) + { + assert(false); + result = NULL; + } + return result; +} + +void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable) +{ + if (mprotect(pStart, size, isExecutable ? (PROT_READ | PROT_EXEC) : (PROT_READ | PROT_WRITE)) == -1) + { + return NULL; + } + + return pStart; +} + +bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ +#ifndef TARGET_OSX + int fd = (int)(size_t)mapperHandle; + mmap(pStart, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset); + memset(pStart, 0, size); +#endif // TARGET_OSX + return munmap(pStart, size) != -1; +} + +void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ +#ifndef TARGET_OSX + int fd = (int)(size_t)mapperHandle; + return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); +#else // TARGET_OSX +#ifdef TARGET_AMD64 + vm_address_t startRW; + vm_prot_t curProtection, maxProtection; + kern_return_t kr = vm_remap(mach_task_self(), &startRW, size, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR, + mach_task_self(), (vm_address_t)pStart, FALSE, &curProtection, &maxProtection, VM_INHERIT_NONE); + + if (kr != KERN_SUCCESS) + { + return NULL; + } + + int st = mprotect((void*)startRW, size, PROT_READ | PROT_WRITE); + if (st == -1) + { + munmap((void*)startRW, size); + return NULL; + } + + return (void*)startRW; +#else // TARGET_AMD64 + // This method should not be called on OSX ARM64 + assert(false); + return NULL; +#endif // TARGET_AMD64 +#endif // TARGET_OSX +} + +bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size) +{ + return munmap(pStart, size) != -1; +} diff --git a/src/coreclr/minipal/Windows/CMakeLists.txt b/src/coreclr/minipal/Windows/CMakeLists.txt new file mode 100644 index 0000000000000..b56b5017d375f --- /dev/null +++ b/src/coreclr/minipal/Windows/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(coreclrminipal + STATIC + doublemapping.cpp +) diff --git a/src/coreclr/minipal/Windows/doublemapping.cpp b/src/coreclr/minipal/Windows/doublemapping.cpp new file mode 100644 index 0000000000000..e265f1d139ad0 --- /dev/null +++ b/src/coreclr/minipal/Windows/doublemapping.cpp @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include +#include "minipal.h" + +#define HIDWORD(_qw) ((ULONG)((_qw) >> 32)) +#define LODWORD(_qw) ((ULONG)(_qw)) + +#ifdef TARGET_64BIT +static const uint64_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024; +#else +static const uint64_t MaxDoubleMappedSize = UINT_MAX; +#endif + +#define VIRTUAL_ALLOC_RESERVE_GRANULARITY (64*1024) // 0x10000 (64 KB) +inline size_t ALIGN_UP( size_t val, size_t alignment ) +{ + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + assert( 0 == (alignment & (alignment - 1)) ); + size_t result = (val + (alignment - 1)) & ~(alignment - 1); + assert( result >= val ); // check for overflow + return result; +} + +template inline T ALIGN_UP(T val, size_t alignment) +{ + return (T)ALIGN_UP((size_t)val, alignment); +} + +inline void *GetTopMemoryAddress(void) +{ + static void *result; // = NULL; + if( NULL == result ) + { + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + result = sysInfo.lpMaximumApplicationAddress; + } + return result; +} + +inline void *GetBotMemoryAddress(void) +{ + static void *result; // = NULL; + if( NULL == result ) + { + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + result = sysInfo.lpMinimumApplicationAddress; + } + return result; +} + +#define TOP_MEMORY (GetTopMemoryAddress()) +#define BOT_MEMORY (GetBotMemoryAddress()) + +bool VMToOSInterface::CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize) +{ + *pMaxExecutableCodeSize = (size_t)MaxDoubleMappedSize; + *pHandle = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_EXECUTE_READWRITE | SEC_RESERVE, // read/write/execute access + HIDWORD(MaxDoubleMappedSize), // maximum object size (high-order DWORD) + LODWORD(MaxDoubleMappedSize), // maximum object size (low-order DWORD) + NULL); + + return *pHandle != NULL; +} + +void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle) +{ + CloseHandle((HANDLE)mapperHandle); +} + +void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *pMinAddr, const void* pMaxAddr) +{ + BYTE *pResult = nullptr; // our return value; + + if (size == 0) + { + return nullptr; + } + + // + // First lets normalize the pMinAddr and pMaxAddr values + // + // If pMinAddr is NULL then set it to BOT_MEMORY + if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY)) + { + pMinAddr = (BYTE *) BOT_MEMORY; + } + + // If pMaxAddr is NULL then set it to TOP_MEMORY + if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY)) + { + pMaxAddr = (BYTE *) TOP_MEMORY; + } + + // If pMaxAddr is not greater than pMinAddr we can not make an allocation + if (pMaxAddr <= pMinAddr) + { + return nullptr; + } + + // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY + // then we can call ClrVirtualAlloc instead + if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY)) + { + return (BYTE*)MapViewOfFile((HANDLE)mapperHandle, + FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size); + } + + // We will do one scan from [pMinAddr .. pMaxAddr] + // First align the tryAddr up to next 64k base address. + // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons. + // + BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY); + bool virtualQueryFailed = false; + bool faultInjected = false; + unsigned virtualQueryCount = 0; + + // Now scan memory and try to find a free block of the size requested. + while ((tryAddr + size) <= (BYTE *) pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbInfo; + + // Use VirtualQuery to find out if this address is MEM_FREE + // + virtualQueryCount++; + if (!VirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo))) + { + // Exit and return nullptr if the VirtualQuery call fails. + virtualQueryFailed = true; + break; + } + + // Is there enough memory free from this start location? + // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0 + if ((mbInfo.State == MEM_FREE) && + (mbInfo.RegionSize >= (SIZE_T) size || mbInfo.RegionSize == 0)) + { + // Try reserving the memory using VirtualAlloc now + pResult = (BYTE*)MapViewOfFileEx((HANDLE)mapperHandle, + FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size, + tryAddr); + + // Normally this will be successful + // + if (pResult != nullptr) + { + // return pResult + break; + } + + // We might fail in a race. So just move on to next region and continue trying + tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY; + } + else + { + // Try another section of memory + tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY, + (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize); + } + } + + return pResult; +} + +void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable) +{ + return VirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READ : PAGE_READWRITE); +} + +bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ + // Zero the memory before the unmapping + VirtualAlloc(pStart, size, MEM_COMMIT, PAGE_READWRITE); + memset(pStart, 0, size); + return UnmapViewOfFile(pStart); +} + +void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ + return (BYTE*)MapViewOfFile((HANDLE)mapperHandle, + FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size); +} + +bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size) +{ + return UnmapViewOfFile(pStart); +} diff --git a/src/coreclr/minipal/minipal.h b/src/coreclr/minipal/minipal.h new file mode 100644 index 0000000000000..39098f9bc1295 --- /dev/null +++ b/src/coreclr/minipal/minipal.h @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +#include + +// Interface between the runtime and platform specific functionality +class VMToOSInterface +{ +private: + ~VMToOSInterface() {} +public: + // Create double mapped memory mapper + // Parameters: + // pHandle - receives handle of the double mapped memory mapper + // pMaxExecutableCodeSize - receives the maximum executable memory size it can map + // Return: + // true if it succeeded, false if it failed + static bool CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize); + + // Destroy the double mapped memory mapper represented by the passed in handle + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to destroy + static void DestroyDoubleMemoryMapper(void *mapperHandle); + + // Reserve a block of memory that can be double mapped. + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // offset - offset in the underlying shared memory + // size - size of the block to reserve + // rangeStart + // rangeEnd - Requests reserving virtual memory in the specified range. + // Setting both rangeStart and rangeEnd to 0 means that the + // requested range is not limited. + // When a specific range is requested, it is obligatory. + // Return: + // starting virtual address of the reserved memory or NULL if it failed + static void* ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd); + + // Commit a block of memory in the range previously reserved by the ReserveDoubleMappedMemory + // Parameters: + // pStart - start address of the virtual address range to commit + // size - size of the memory block to commit + // isExecutable - true means that the mapping should be RX, false means RW + // Return: + // Committed range start + static void* CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable); + + // Release a block of virtual memory previously commited by the CommitDoubleMappedMemory + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // pStart - start address of the virtual address range to release. It must be one + // that was previously returned by the CommitDoubleMappedMemory + // offset - offset in the underlying shared memory + // size - size of the memory block to release + // Return: + // true if it succeeded, false if it failed + static bool ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size); + + // Get a RW mapping for the RX block specified by the arguments + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // pStart - start address of the RX virtual address range. + // offset - offset in the underlying shared memory + // size - size of the memory block to map as RW + // Return: + // Starting virtual address of the RW mapping. + static void* GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size); + + // Release RW mapping of the block specified by the arguments + // Parameters: + // pStart - Start address of the RW virtual address range. It must be an address + // previously returned by the GetRWMapping. + // size - Size of the memory block to release. It must be the size previously + // passed to the GetRWMapping that returned the pStart. + // Return: + // true if it succeeded, false if it failed + static bool ReleaseRWMapping(void* pStart, size_t size); +}; diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index 1ae433adbfd89..8c57742cb6315 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -69,6 +69,7 @@ endif(CLR_CMAKE_TARGET_WIN32) set(UTILCODE_SOURCES ${UTILCODE_COMMON_SOURCES} + executableallocator.cpp ) set(UTILCODE_DAC_SOURCES diff --git a/src/coreclr/utilcode/executableallocator.cpp b/src/coreclr/utilcode/executableallocator.cpp new file mode 100644 index 0000000000000..ac4c326c83784 --- /dev/null +++ b/src/coreclr/utilcode/executableallocator.cpp @@ -0,0 +1,755 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pedecoder.h" +#include "executableallocator.h" + +#if USE_UPPER_ADDRESS +// Preferred region to allocate the code in. +BYTE * ExecutableAllocator::g_codeMinAddr; +BYTE * ExecutableAllocator::g_codeMaxAddr; +BYTE * ExecutableAllocator::g_codeAllocStart; +// Next address to try to allocate for code in the preferred region. +BYTE * ExecutableAllocator::g_codeAllocHint; +#endif // USE_UPPER_ADDRESS + +bool ExecutableAllocator::g_isWXorXEnabled = false; + +ExecutableAllocator::FatalErrorHandler ExecutableAllocator::g_fatalErrorHandler = NULL; + +ExecutableAllocator* ExecutableAllocator::g_instance = NULL; + +bool ExecutableAllocator::IsDoubleMappingEnabled() +{ + LIMITED_METHOD_CONTRACT; + +#if defined(HOST_OSX) && defined(HOST_ARM64) + return false; +#else + return g_isWXorXEnabled; +#endif +} + +bool ExecutableAllocator::IsWXORXEnabled() +{ + LIMITED_METHOD_CONTRACT; + +#if defined(HOST_OSX) && defined(HOST_ARM64) + return true; +#else + return g_isWXorXEnabled; +#endif +} + +extern SYSTEM_INFO g_SystemInfo; + +size_t ExecutableAllocator::Granularity() +{ + LIMITED_METHOD_CONTRACT; + + return g_SystemInfo.dwAllocationGranularity; +} + +// Use this function to initialize the g_codeAllocHint +// during startup. base is runtime .dll base address, +// size is runtime .dll virtual size. +void ExecutableAllocator::InitCodeAllocHint(size_t base, size_t size, int randomPageOffset) +{ +#if USE_UPPER_ADDRESS + +#ifdef _DEBUG + // If GetForceRelocs is enabled we don't constrain the pMinAddr + if (PEDecoder::GetForceRelocs()) + return; +#endif + + // + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any code heap that doesn't specify an address + // range using [pMinAddr..pMaxAddr] we place it in the + // upper address space + // This enables us to avoid having to use long JumpStubs + // to reach the code for our ngen-ed images. + // Which are also placed in the UPPER_ADDRESS space. + // + SIZE_T reach = 0x7FFF0000u; + + // We will choose the preferred code region based on the address of clr.dll. The JIT helpers + // in clr.dll are the most heavily called functions. + g_codeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0; + g_codeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1; + + BYTE * pStart; + + if (g_codeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS && + (BYTE *)CODEHEAP_START_ADDRESS < g_codeMaxAddr) + { + // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista) + // Use the code head start address that does not cause collisions with NGen images. + // This logic is coupled with scripts that we use to assign base addresses. + pStart = (BYTE *)CODEHEAP_START_ADDRESS; + } + else + if (base > UINT32_MAX) + { + // clr.dll got address assigned by ASLR? + // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned + // addresses. Do not start at g_codeMinAddr exactly so that we can also reach common native images + // that can be placed at higher addresses than clr.dll. + pStart = g_codeMinAddr + (g_codeMaxAddr - g_codeMinAddr) / 8; + } + else + { + // clr.dll missed the base address? + // Try to occupy the space right after it. + pStart = (BYTE *)(base + size); + } + + // Randomize the address space + pStart += GetOsPageSize() * randomPageOffset; + + g_codeAllocStart = pStart; + g_codeAllocHint = pStart; +#endif +} + +// Use this function to reset the g_codeAllocHint +// after unloading an AppDomain +void ExecutableAllocator::ResetCodeAllocHint() +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + g_codeAllocHint = g_codeAllocStart; +#endif +} + +// Returns TRUE if p is located in near clr.dll that allows us +// to use rel32 IP-relative addressing modes. +bool ExecutableAllocator::IsPreferredExecutableRange(void * p) +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + if (g_codeMinAddr <= (BYTE *)p && (BYTE *)p < g_codeMaxAddr) + return true; +#endif + return false; +} + +ExecutableAllocator* ExecutableAllocator::Instance() +{ + LIMITED_METHOD_CONTRACT; + return g_instance; +} + +ExecutableAllocator::~ExecutableAllocator() +{ + if (IsDoubleMappingEnabled()) + { + VMToOSInterface::DestroyDoubleMemoryMapper(m_doubleMemoryMapperHandle); + } +} + +HRESULT ExecutableAllocator::StaticInitialize(FatalErrorHandler fatalErrorHandler) +{ + LIMITED_METHOD_CONTRACT; + + g_fatalErrorHandler = fatalErrorHandler; + g_isWXorXEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableWriteXorExecute) != 0; + g_instance = new (nothrow) ExecutableAllocator(); + if (g_instance == NULL) + { + return E_OUTOFMEMORY; + } + + if (!g_instance->Initialize()) + { + return E_FAIL; + } + + return S_OK; +} + +bool ExecutableAllocator::Initialize() +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + if (!VMToOSInterface::CreateDoubleMemoryMapper(&m_doubleMemoryMapperHandle, &m_maxExecutableCodeSize)) + { + return false; + } + + m_CriticalSection = ClrCreateCriticalSection(CrstExecutableAllocatorLock,CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); + } + + return true; +} + +//#define ENABLE_CACHED_MAPPINGS + +void ExecutableAllocator::UpdateCachedMapping(BlockRW* pBlock) +{ + LIMITED_METHOD_CONTRACT; +#ifdef ENABLE_CACHED_MAPPINGS + if (m_cachedMapping == NULL) + { + m_cachedMapping = pBlock; + pBlock->refCount++; + } + else if (m_cachedMapping != pBlock) + { + void* unmapAddress = NULL; + size_t unmapSize; + + if (!RemoveRWBlock(m_cachedMapping->baseRW, &unmapAddress, &unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found")); + } + if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed")); + } + m_cachedMapping = pBlock; + pBlock->refCount++; + } +#endif // ENABLE_CACHED_MAPPINGS +} + +void* ExecutableAllocator::FindRWBlock(void* baseRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next) + { + if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + pBlock->refCount++; + UpdateCachedMapping(pBlock); + + return (BYTE*)pBlock->baseRW + ((size_t)baseRX - (size_t)pBlock->baseRX); + } + } + + return NULL; +} + +bool ExecutableAllocator::AddRWBlock(void* baseRW, void* baseRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next) + { + if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + break; + } + } + + // The new "nothrow" below failure is handled as fail fast since it is not recoverable + PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure); + + BlockRW* pBlockRW = new (nothrow) BlockRW(); + if (pBlockRW == NULL) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block metadata cannot be allocated")); + return false; + } + + pBlockRW->baseRW = baseRW; + pBlockRW->baseRX = baseRX; + pBlockRW->size = size; + pBlockRW->next = m_pFirstBlockRW; + pBlockRW->refCount = 1; + m_pFirstBlockRW = pBlockRW; + + UpdateCachedMapping(pBlockRW); + + return true; +} + +bool ExecutableAllocator::RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize) +{ + LIMITED_METHOD_CONTRACT; + + BlockRW* pPrevBlockRW = NULL; + for (BlockRW* pBlockRW = m_pFirstBlockRW; pBlockRW != NULL; pBlockRW = pBlockRW->next) + { + if (pBlockRW->baseRW <= pRW && (size_t)pRW < ((size_t)pBlockRW->baseRW + pBlockRW->size)) + { + // found + pBlockRW->refCount--; + if (pBlockRW->refCount != 0) + { + *pUnmapAddress = NULL; + return true; + } + + if (pPrevBlockRW == NULL) + { + m_pFirstBlockRW = pBlockRW->next; + } + else + { + pPrevBlockRW->next = pBlockRW->next; + } + + *pUnmapAddress = pBlockRW->baseRW; + *pUnmapSize = pBlockRW->size; + + delete pBlockRW; + return true; + } + + pPrevBlockRW = pBlockRW; + } + + return false; +} + +bool ExecutableAllocator::AllocateOffset(size_t* pOffset, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + size_t offset = m_freeOffset; + size_t newFreeOffset = offset + size; + + if (newFreeOffset > m_maxExecutableCodeSize) + { + return false; + } + + m_freeOffset = newFreeOffset; + + *pOffset = offset; + + return true; +} + +void ExecutableAllocator::AddRXBlock(BlockRX* pBlock) +{ + LIMITED_METHOD_CONTRACT; + + pBlock->next = m_pFirstBlockRX; + m_pFirstBlockRX = pBlock; +} + +void* ExecutableAllocator::Commit(void* pStart, size_t size, bool isExecutable) +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + return VMToOSInterface::CommitDoubleMappedMemory(pStart, size, isExecutable); + } + else + { + return ClrVirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + } +} + +void ExecutableAllocator::Release(void* pRX) +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + // Locate the RX block corresponding to the pRX and remove it from the linked list + BlockRX* pBlock; + BlockRX* pPrevBlock = NULL; + + for (pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next) + { + if (pRX == pBlock->baseRX) + { + if (pPrevBlock == NULL) + { + m_pFirstBlockRX = pBlock->next; + } + else + { + pPrevBlock->next = pBlock->next; + } + + break; + } + pPrevBlock = pBlock; + } + + if (pBlock != NULL) + { + VMToOSInterface::ReleaseDoubleMappedMemory(m_doubleMemoryMapperHandle, pRX, pBlock->offset, pBlock->size); + // Put the released block into the free block list + pBlock->baseRX = NULL; + pBlock->next = m_pFirstFreeBlockRX; + m_pFirstFreeBlockRX = pBlock; + } + else + { + // The block was not found, which should never happen. + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to release was not found")); + } + } + else + { + ClrVirtualFree(pRX, 0, MEM_RELEASE); + } +} + +// Find a free block with the closest size >= the requested size. +// Returns NULL if no such block exists. +ExecutableAllocator::BlockRX* ExecutableAllocator::FindBestFreeBlock(size_t size) +{ + LIMITED_METHOD_CONTRACT; + + BlockRX* pPrevBlock = NULL; + BlockRX* pPrevBestBlock = NULL; + BlockRX* pBestBlock = NULL; + BlockRX* pBlock = m_pFirstFreeBlockRX; + + while (pBlock != NULL) + { + if (pBlock->size >= size) + { + if (pBestBlock != NULL) + { + if (pBlock->size < pBestBlock->size) + { + pPrevBestBlock = pPrevBlock; + pBestBlock = pBlock; + } + } + else + { + pPrevBestBlock = pPrevBlock; + pBestBlock = pBlock; + } + } + pPrevBlock = pBlock; + pBlock = pBlock->next; + } + + if (pBestBlock != NULL) + { + if (pPrevBestBlock != NULL) + { + pPrevBestBlock->next = pBestBlock->next; + } + else + { + m_pFirstFreeBlockRX = pBestBlock->next; + } + + pBestBlock->next = NULL; + } + + return pBestBlock; +} + +// Allocate a new block of executable memory and the related descriptor structure. +// First try to get it from the free blocks and if there is no suitable free block, +// allocate a new one. +ExecutableAllocator::BlockRX* ExecutableAllocator::AllocateBlock(size_t size, bool* pIsFreeBlock) +{ + LIMITED_METHOD_CONTRACT; + + size_t offset; + BlockRX* block = FindBestFreeBlock(size); + *pIsFreeBlock = (block != NULL); + + if (block == NULL) + { + if (!AllocateOffset(&offset, size)) + { + return NULL; + } + + block = new (nothrow) BlockRX(); + if (block == NULL) + { + return NULL; + } + + block->offset = offset; + block->size = size; + } + + return block; +} + +// Backout a previously allocated block. The block is added to the free blocks list and +// reused for later allocation requests. +void ExecutableAllocator::BackoutBlock(BlockRX* pBlock, bool isFreeBlock) +{ + LIMITED_METHOD_CONTRACT; + + if (!isFreeBlock) + { + m_freeOffset -= pBlock->size; + delete pBlock; + } + else + { + pBlock->next = m_pFirstFreeBlockRX; + m_pFirstFreeBlockRX = pBlock; + } +} + +// Reserve executable memory within the specified virtual address space range. If it is not possible to +// reserve memory in that range, the method returns NULL and nothing is allocated. +void* ExecutableAllocator::ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + void *result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, loAddress, hiAddress); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + + return result; + } + else + { + DWORD allocationType = MEM_RESERVE; +#ifdef HOST_UNIX + // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. + // This will allow us to place JIT'ed code close to the coreclr library + // and thus improve performance by avoiding jump stubs in managed code. + allocationType |= MEM_RESERVE_EXECUTABLE; +#endif + return ClrVirtualAllocWithinRange((const BYTE*)loAddress, (const BYTE*)hiAddress, size, allocationType, PAGE_NOACCESS); + } +} + +// Reserve executable memory. On Windows it tries to use the allocation hints to +// allocate memory close to the previously allocated executable memory and loaded +// executable files. +void* ExecutableAllocator::Reserve(size_t size) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + + BYTE *result = NULL; + +#if USE_UPPER_ADDRESS + // + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any heap that will contain executable code + // we will place it in the upper address space + // + // This enables us to avoid having to use JumpStubs + // to reach the code for our ngen-ed images on x64, + // since they are also placed in the UPPER_ADDRESS space. + // + BYTE * pHint = g_codeAllocHint; + + if (size <= (SIZE_T)(g_codeMaxAddr - g_codeMinAddr) && pHint != NULL) + { + // Try to allocate in the preferred region after the hint + result = (BYTE*)ReserveWithinRange(size, pHint, g_codeMaxAddr); + if (result != NULL) + { + g_codeAllocHint = result + size; + } + else + { + // Try to allocate in the preferred region before the hint + result = (BYTE*)ReserveWithinRange(size, g_codeMinAddr, pHint + size); + + if (result != NULL) + { + g_codeAllocHint = result + size; + } + + g_codeAllocHint = NULL; + } + } + + // Fall through to +#endif // USE_UPPER_ADDRESS + + if (result == NULL) + { + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + result = (BYTE*)VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, 0, 0); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + } + else + { + DWORD allocationType = MEM_RESERVE; +#ifdef HOST_UNIX + // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. + // This will allow us to place JIT'ed code close to the coreclr library + // and thus improve performance by avoiding jump stubs in managed code. + allocationType |= MEM_RESERVE_EXECUTABLE; +#endif + result = (BYTE*)ClrVirtualAlloc(NULL, size, allocationType, PAGE_NOACCESS); + } + } + + return result; +} + +// Reserve a block of executable memory at the specified virtual address. If it is not +// possible, the method returns NULL. +void* ExecutableAllocator::ReserveAt(void* baseAddressRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + void* result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, baseAddressRX, baseAddressRX); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + + return result; + } + else + { + return VirtualAlloc(baseAddressRX, size, MEM_RESERVE, PAGE_NOACCESS); + } +} + +// Map an executable memory block as writeable. If there is already a mapping +// covering the specified block, return that mapping instead of creating a new one. +// Return starting address of the writeable mapping. +void* ExecutableAllocator::MapRW(void* pRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + if (!IsDoubleMappingEnabled()) + { + return pRX; + } + + CRITSEC_Holder csh(m_CriticalSection); + + void* result = FindRWBlock(pRX, size); + if (result != NULL) + { + return result; + } + + for (BlockRX* pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next) + { + if (pRX >= pBlock->baseRX && ((size_t)pRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + // Offset of the RX address in the originally allocated block + size_t offset = (size_t)pRX - (size_t)pBlock->baseRX; + // Offset of the RX address that will start the newly mapped block + size_t mapOffset = ALIGN_DOWN(offset, Granularity()); + // Size of the block we will map + size_t mapSize = ALIGN_UP(offset - mapOffset + size, Granularity()); + void* pRW = VMToOSInterface::GetRWMapping(m_doubleMemoryMapperHandle, (BYTE*)pBlock->baseRX + mapOffset, pBlock->offset + mapOffset, mapSize); + + if (pRW == NULL) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Failed to create RW mapping for RX memory")); + } + + AddRWBlock(pRW, (BYTE*)pBlock->baseRX + mapOffset, mapSize); + + return (void*)((size_t)pRW + (offset - mapOffset)); + } + else if (pRX >= pBlock->baseRX && pRX < (void*)((size_t)pBlock->baseRX + pBlock->size)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to RW map a block that crosses the end of the allocated RX range")); + } + else if (pRX < pBlock->baseRX && (void*)((size_t)pRX + size) > pBlock->baseRX) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to map a block that crosses the beginning of the allocated range")); + } + } + + // The executable memory block was not found, so we cannot provide the writeable mapping. + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to map as RW was not found")); + return NULL; +} + +// Unmap writeable mapping at the specified address. The address must be an address +// returned by the MapRW method. +void ExecutableAllocator::UnmapRW(void* pRW) +{ + LIMITED_METHOD_CONTRACT; + + if (!IsDoubleMappingEnabled()) + { + return; + } + + CRITSEC_Holder csh(m_CriticalSection); + _ASSERTE(pRW != NULL); + + void* unmapAddress = NULL; + size_t unmapSize; + + if (!RemoveRWBlock(pRW, &unmapAddress, &unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found")); + } + + if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed")); + } +} diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp index adaf07d8f5825..b3b381b2f9bef 100644 --- a/src/coreclr/utilcode/loaderheap.cpp +++ b/src/coreclr/utilcode/loaderheap.cpp @@ -695,15 +695,21 @@ size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap); struct LoaderHeapFreeBlock { public: - LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list - size_t m_dwSize; // Total size of this block (including this header) -//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations. + LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list + size_t m_dwSize; // Total size of this block + void *m_pBlockAddress; // Virtual address of the block +#ifndef DACCESS_COMPILE static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; + // The new "nothrow" below failure is handled in a non-fault way, so + // make sure that callers with FORBID_FAULT can call this method without + // firing the contract violation assert. + PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure); + LOADER_HEAP_BEGIN_TRAP_FAULT // It's illegal to insert a free block that's smaller than the minimum sized allocation - @@ -722,19 +728,30 @@ struct LoaderHeapFreeBlock } #endif - INDEBUG(memset(pMem, 0xcc, dwTotalSize);) - LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem; - pNewBlock->m_pNext = *ppHead; - pNewBlock->m_dwSize = dwTotalSize; - *ppHead = pNewBlock; + void* pMemRW = pMem; + ExecutableWriterHolder memWriterHolder; + if (pHeap->IsExecutable()) + { + memWriterHolder = ExecutableWriterHolder(pMem, dwTotalSize); + pMemRW = memWriterHolder.GetRW(); + } - MergeBlock(pNewBlock, pHeap); + INDEBUG(memset(pMemRW, 0xcc, dwTotalSize);) + LoaderHeapFreeBlock *pNewBlock = new (nothrow) LoaderHeapFreeBlock; + // If we fail allocating the LoaderHeapFreeBlock, ignore the failure and don't insert the free block at all. + if (pNewBlock != NULL) + { + pNewBlock->m_pNext = *ppHead; + pNewBlock->m_dwSize = dwTotalSize; + pNewBlock->m_pBlockAddress = pMem; + *ppHead = pNewBlock; + MergeBlock(pNewBlock, pHeap); + } LOADER_HEAP_END_TRAP_FAULT } - - static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap) + static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, UnlockedLoaderHeap *pHeap) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; @@ -751,23 +768,19 @@ struct LoaderHeapFreeBlock size_t dwCurSize = pCur->m_dwSize; if (dwCurSize == dwSize) { - pResult = pCur; + pResult = pCur->m_pBlockAddress; // Exact match. Hooray! - if (fRemoveFromFreeList) - { - *ppWalk = pCur->m_pNext; - } + *ppWalk = pCur->m_pNext; + delete pCur; break; } else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap)) { // Partial match. Ok... - pResult = pCur; - if (fRemoveFromFreeList) - { - *ppWalk = pCur->m_pNext; - InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap ); - } + pResult = pCur->m_pBlockAddress; + *ppWalk = pCur->m_pNext; + InsertFreeBlock(ppWalk, ((BYTE*)pCur->m_pBlockAddress) + dwSize, dwCurSize - dwSize, pHeap ); + delete pCur; break; } @@ -777,19 +790,22 @@ struct LoaderHeapFreeBlock ppWalk = &( pCur->m_pNext ); } - if (pResult && fRemoveFromFreeList) + if (pResult) { + void *pResultRW = pResult; + ExecutableWriterHolder resultWriterHolder; + if (pHeap->IsExecutable()) + { + resultWriterHolder = ExecutableWriterHolder(pResult, dwSize); + pResultRW = resultWriterHolder.GetRW(); + } // Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant! - memset(pResult, 0, dwSize); + memset(pResultRW, 0, dwSize); } LOADER_HEAP_END_TRAP_FAULT return pResult; - - - } - private: // Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened. static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap) @@ -803,7 +819,7 @@ struct LoaderHeapFreeBlock LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext; size_t dwSize = pFreeBlock->m_dwSize; - if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize)) + if (pNextBlock == NULL || ((BYTE*)pNextBlock->m_pBlockAddress) != (((BYTE*)pFreeBlock->m_pBlockAddress) + dwSize)) { result = FALSE; } @@ -811,9 +827,17 @@ struct LoaderHeapFreeBlock { size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize; LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext; - INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);) + void *pMemRW = pFreeBlock->m_pBlockAddress; + ExecutableWriterHolder memWriterHolder; + if (pHeap->IsExecutable()) + { + memWriterHolder = ExecutableWriterHolder(pFreeBlock->m_pBlockAddress, dwCombinedSize); + pMemRW = memWriterHolder.GetRW(); + } + INDEBUG(memset(pMemRW, 0xcc, dwCombinedSize);) pFreeBlock->m_pNext = pNextNextBlock; pFreeBlock->m_dwSize = dwCombinedSize; + delete pNextBlock; result = TRUE; } @@ -822,7 +846,7 @@ struct LoaderHeapFreeBlock return result; } - +#endif // DACCESS_COMPILE }; @@ -840,8 +864,7 @@ struct LoaderHeapFreeBlock // - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte) // - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag) // -// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock) -// - c bytes of pad (where "c" is just enough to pointer-align the following byte) +// - b bytes of pad (where "b" is just enough to pointer-align the following byte) // // ==> Following address is always pointer-aligned //===================================================================================== @@ -862,10 +885,6 @@ inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHe #ifdef _DEBUG dwSize += sizeof(LoaderHeapValidationTag); #endif - if (dwSize < sizeof(LoaderHeapFreeBlock)) - { - dwSize = sizeof(LoaderHeapFreeBlock); - } } dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); @@ -977,9 +996,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap() if (fReleaseMemory) { - BOOL fSuccess; - fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE); - _ASSERTE(fSuccess); + ExecutableAllocator::Instance()->Release(pVirtualAddress); } delete pSearch; @@ -987,9 +1004,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap() if (m_reservedBlock.m_fReleaseMemory) { - BOOL fSuccess; - fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE); - _ASSERTE(fSuccess); + ExecutableAllocator::Instance()->Release(m_reservedBlock.pVirtualAddress); } INDEBUG(s_dwNumInstancesOfLoaderHeaps --;) @@ -1058,7 +1073,7 @@ void ReleaseReservedMemory(BYTE* value) { if (value) { - ClrVirtualFree(value, 0, MEM_RELEASE); + ExecutableAllocator::Instance()->Release(value); } } @@ -1114,7 +1129,9 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit) // Reserve pages // - pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS); + // Reserve the memory for even non-executable stuff close to the executable code, as it has profound effect + // on e.g. a static variable access performance. + pData = (BYTE *)ExecutableAllocator::Instance()->Reserve(dwSizeToReserve); if (pData == NULL) { return FALSE; @@ -1140,7 +1157,7 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit) } // Commit first set of pages, since it will contain the LoaderHeapBlock - void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + void *pTemp = ExecutableAllocator::Instance()->Commit(pData, dwSizeToCommit, (m_Options & LHF_EXECUTABLE)); if (pTemp == NULL) { //_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap"); @@ -1213,7 +1230,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize) dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize()); // Yes, so commit the desired number of reserved pages - void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + void *pData = ExecutableAllocator::Instance()->Commit(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, (m_Options & LHF_EXECUTABLE)); if (pData == NULL) return FALSE; @@ -1316,7 +1333,7 @@ void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize { // Any memory available on the free list? - void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this); + void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, this); if (!pData) { // Enough bytes available in committed region? @@ -1518,8 +1535,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize )) { - // Cool. This was the last block allocated. We can just undo the allocation instead - // of going to the freelist. void *pMemRW = pMem; ExecutableWriterHolder memWriterHolder; if (m_Options & LHF_EXECUTABLE) @@ -1527,6 +1542,9 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, memWriterHolder = ExecutableWriterHolder(pMem, dwSize); pMemRW = memWriterHolder.GetRW(); } + + // Cool. This was the last block allocated. We can just undo the allocation instead + // of going to the freelist. memset(pMemRW, 0x00, dwSize); // Fill freed region with 0 m_pAllocPtr = (BYTE*)pMem; } @@ -1534,7 +1552,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, { LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this); } - } diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 0026d1f619f14..e7b1755b2b1c4 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -352,168 +352,6 @@ HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid, return hr; } -#if USE_UPPER_ADDRESS -static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in. -static BYTE * s_CodeMaxAddr; -static BYTE * s_CodeAllocStart; -static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region. -#endif - -// -// Use this function to initialize the s_CodeAllocHint -// during startup. base is runtime .dll base address, -// size is runtime .dll virtual size. -// -void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset) -{ -#if USE_UPPER_ADDRESS - -#ifdef _DEBUG - // If GetForceRelocs is enabled we don't constrain the pMinAddr - if (PEDecoder::GetForceRelocs()) - return; -#endif - -// - // If we are using the UPPER_ADDRESS space (on Win64) - // then for any code heap that doesn't specify an address - // range using [pMinAddr..pMaxAddr] we place it in the - // upper address space - // This enables us to avoid having to use long JumpStubs - // to reach the code for our ngen-ed images. - // Which are also placed in the UPPER_ADDRESS space. - // - SIZE_T reach = 0x7FFF0000u; - - // We will choose the preferred code region based on the address of clr.dll. The JIT helpers - // in clr.dll are the most heavily called functions. - s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0; - s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1; - - BYTE * pStart; - - if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS && - (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr) - { - // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista) - // Use the code head start address that does not cause collisions with NGen images. - // This logic is coupled with scripts that we use to assign base addresses. - pStart = (BYTE *)CODEHEAP_START_ADDRESS; - } - else - if (base > UINT32_MAX) - { - // clr.dll got address assigned by ASLR? - // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned - // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images - // that can be placed at higher addresses than clr.dll. - pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8; - } - else - { - // clr.dll missed the base address? - // Try to occupy the space right after it. - pStart = (BYTE *)(base + size); - } - - // Randomize the address space - pStart += GetOsPageSize() * randomPageOffset; - - s_CodeAllocStart = pStart; - s_CodeAllocHint = pStart; -#endif -} - -// -// Use this function to reset the s_CodeAllocHint -// after unloading an AppDomain -// -void ResetCodeAllocHint() -{ - LIMITED_METHOD_CONTRACT; -#if USE_UPPER_ADDRESS - s_CodeAllocHint = s_CodeAllocStart; -#endif -} - -// -// Returns TRUE if p is located in near clr.dll that allows us -// to use rel32 IP-relative addressing modes. -// -BOOL IsPreferredExecutableRange(void * p) -{ - LIMITED_METHOD_CONTRACT; -#if USE_UPPER_ADDRESS - if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr) - return TRUE; -#endif - return FALSE; -} - -// -// Allocate free memory that will be used for executable code -// Handles the special requirements that we have on 64-bit platforms -// where we want the executable memory to be located near clr.dll -// -BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize, - DWORD flAllocationType, - DWORD flProtect) -{ - CONTRACTL - { - NOTHROW; - } - CONTRACTL_END; - -#if USE_UPPER_ADDRESS - // - // If we are using the UPPER_ADDRESS space (on Win64) - // then for any heap that will contain executable code - // we will place it in the upper address space - // - // This enables us to avoid having to use JumpStubs - // to reach the code for our ngen-ed images on x64, - // since they are also placed in the UPPER_ADDRESS space. - // - BYTE * pHint = s_CodeAllocHint; - - if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL) - { - // Try to allocate in the preferred region after the hint - BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect); - - if (pResult != NULL) - { - s_CodeAllocHint = pResult + dwSize; - return pResult; - } - - // Try to allocate in the preferred region before the hint - pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect); - - if (pResult != NULL) - { - s_CodeAllocHint = pResult + dwSize; - return pResult; - } - - s_CodeAllocHint = NULL; - } - - // Fall through to -#endif // USE_UPPER_ADDRESS - -#ifdef HOST_UNIX - // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. - // This will allow us to place JIT'ed code close to the coreclr library - // and thus improve performance by avoiding jump stubs in managed code. - flAllocationType |= MEM_RESERVE_EXECUTABLE; -#endif // HOST_UNIX - - return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect); - -} - // // Allocate free memory with specific alignment. // diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 1d682d2a428bb..9c2cb3df0b7e9 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -833,7 +833,6 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) set(VM_SOURCES_DAC_AND_WKS_ARCH ${ARCH_SOURCES_DIR}/exceparm.cpp ${ARCH_SOURCES_DIR}/stubs.cpp - ${ARCH_SOURCES_DIR}/armsinglestepper.cpp ) set(VM_HEADERS_DAC_AND_WKS_ARCH @@ -844,6 +843,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) set(VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/profiler.cpp + ${ARCH_SOURCES_DIR}/armsinglestepper.cpp exceptionhandling.cpp gcinfodecoder.cpp ) @@ -868,7 +868,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64) ) if(CLR_CMAKE_HOST_UNIX) - list(APPEND VM_SOURCES_DAC_AND_WKS_ARCH + list(APPEND VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/arm64singlestepper.cpp ) endif(CLR_CMAKE_HOST_UNIX) diff --git a/src/coreclr/vm/amd64/JitHelpers_Fast.asm b/src/coreclr/vm/amd64/JitHelpers_Fast.asm index 82a301bb0cbd1..219597eb350c2 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Fast.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Fast.asm @@ -51,37 +51,6 @@ endif extern JIT_InternalThrow:proc -; There is an even more optimized version of these helpers possible which takes -; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 -; that check (this is more significant in the JIT_WriteBarrier case). -; -; Additionally we can look into providing helpers which will take the src/dest from -; specific registers (like x86) which _could_ (??) make for easier register allocation -; for the JIT64, however it might lead to having to have some nasty code that treats -; these guys really special like... :(. -; -; Version that does the move, checks whether or not it's in the GC and whether or not -; it needs to have it's card updated -; -; void JIT_CheckedWriteBarrier(Object** dst, Object* src) -LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT - - ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference - ; but if it isn't then it will just return. - ; - ; See if this is in GCHeap - cmp rcx, [g_lowest_address] - jb NotInHeap - cmp rcx, [g_highest_address] - jnb NotInHeap - - jmp JIT_WriteBarrier - - NotInHeap: - ; See comment above about possible AV - mov [rcx], rdx - ret -LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT ; Mark start of the code region that we patch at runtime LEAF_ENTRY JIT_PatchedCodeStart, _TEXT @@ -99,7 +68,8 @@ LEAF_ENTRY JIT_WriteBarrier, _TEXT ifdef _DEBUG ; In debug builds, this just contains jump to the debug version of the write barrier by default - jmp JIT_WriteBarrier_Debug + mov rax, JIT_WriteBarrier_Debug + jmp rax endif ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP @@ -388,6 +358,51 @@ endif ret LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT +Section segment para 'DATA' + + align 16 + + public JIT_WriteBarrier_Loc +JIT_WriteBarrier_Loc: + dq 0 + +LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT + ; JIT_WriteBarrier(Object** dst, Object* src) + jmp QWORD PTR [JIT_WriteBarrier_Loc] +LEAF_END JIT_WriteBarrier_Callable, _TEXT + +; There is an even more optimized version of these helpers possible which takes +; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 +; that check (this is more significant in the JIT_WriteBarrier case). +; +; Additionally we can look into providing helpers which will take the src/dest from +; specific registers (like x86) which _could_ (??) make for easier register allocation +; for the JIT64, however it might lead to having to have some nasty code that treats +; these guys really special like... :(. +; +; Version that does the move, checks whether or not it's in the GC and whether or not +; it needs to have it's card updated +; +; void JIT_CheckedWriteBarrier(Object** dst, Object* src) +LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT + + ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference + ; but if it isn't then it will just return. + ; + ; See if this is in GCHeap + cmp rcx, [g_lowest_address] + jb NotInHeap + cmp rcx, [g_highest_address] + jnb NotInHeap + + jmp QWORD PTR [JIT_WriteBarrier_Loc] + + NotInHeap: + ; See comment above about possible AV + mov [rcx], rdx + ret +LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT + ; The following helper will access ("probe") a word on each page of the stack ; starting with the page right beneath rsp down to the one pointed to by r11. ; The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. diff --git a/src/coreclr/vm/amd64/jithelpers_fast.S b/src/coreclr/vm/amd64/jithelpers_fast.S index a13afb4878511..8109886d0c969 100644 --- a/src/coreclr/vm/amd64/jithelpers_fast.S +++ b/src/coreclr/vm/amd64/jithelpers_fast.S @@ -32,26 +32,14 @@ LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT // See if this is in GCHeap PREPARE_EXTERNAL_VAR g_lowest_address, rax cmp rdi, [rax] -#ifdef FEATURE_WRITEBARRIER_COPY // jb NotInHeap .byte 0x72, 0x12 -#else - // jb NotInHeap - .byte 0x72, 0x0e -#endif PREPARE_EXTERNAL_VAR g_highest_address, rax cmp rdi, [rax] -#ifdef FEATURE_WRITEBARRIER_COPY // jnb NotInHeap .byte 0x73, 0x06 jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)] -#else - // jnb NotInHeap - .byte 0x73, 0x02 - // jmp C_FUNC(JIT_WriteBarrier) - .byte 0xeb, 0x05 -#endif NotInHeap: // See comment above about possible AV @@ -398,11 +386,17 @@ LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT ret LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT -#ifdef FEATURE_WRITEBARRIER_COPY // When JIT_WriteBarrier is copied into an allocated page, // helpers use this global variable to jump to it. This variable is set in InitThreadManager. - .global _JIT_WriteBarrier_Loc - .zerofill __DATA,__common,_JIT_WriteBarrier_Loc,8,3 + .global C_FUNC(JIT_WriteBarrier_Loc) +#ifdef TARGET_OSX + .zerofill __DATA,__common,C_FUNC(JIT_WriteBarrier_Loc),8,3 +#else + .data + C_FUNC(JIT_WriteBarrier_Loc): + .quad 0 + .text +#endif // ------------------------------------------------------------------ // __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) @@ -412,8 +406,6 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)] LEAF_END JIT_WriteBarrier_Callable, _TEXT -#endif // FEATURE_WRITEBARRIER_COPY - // The following helper will access ("probe") a word on each page of the stack // starting with the page right beneath rsp down to the one pointed to by r11. diff --git a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp index 38bff78a54cb0..02b023777b8a9 100644 --- a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp +++ b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp @@ -293,7 +293,10 @@ int WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier, // the memcpy must come before the switch statment because the asserts inside the switch // are actually looking into the JIT_WriteBarrier buffer - memcpy(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize()); + { + ExecutableWriterHolder writeBarrierWriterHolder(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), GetCurrentWriteBarrierSize()); + memcpy(writeBarrierWriterHolder.GetRW(), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize()); + } switch (newWriteBarrier) { @@ -544,7 +547,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended) // Change immediate if different from new g_ephermeral_high. if (*(UINT64*)m_pUpperBoundImmediate != (size_t)g_ephemeral_high) { - *(UINT64*)m_pUpperBoundImmediate = (size_t)g_ephemeral_high; + ExecutableWriterHolder upperBoundWriterHolder((UINT64*)m_pUpperBoundImmediate, sizeof(UINT64)); + *upperBoundWriterHolder.GetRW() = (size_t)g_ephemeral_high; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } } @@ -557,7 +561,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended) // Change immediate if different from new g_ephermeral_low. if (*(UINT64*)m_pLowerBoundImmediate != (size_t)g_ephemeral_low) { - *(UINT64*)m_pLowerBoundImmediate = (size_t)g_ephemeral_low; + ExecutableWriterHolder lowerBoundImmediateWriterHolder((UINT64*)m_pLowerBoundImmediate, sizeof(UINT64)); + *lowerBoundImmediateWriterHolder.GetRW() = (size_t)g_ephemeral_low; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } break; @@ -609,7 +614,8 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus #endif // FEATURE_SVR_GC if (*(UINT64*)m_pWriteWatchTableImmediate != (size_t)g_sw_ww_table) { - *(UINT64*)m_pWriteWatchTableImmediate = (size_t)g_sw_ww_table; + ExecutableWriterHolder writeWatchTableImmediateWriterHolder((UINT64*)m_pWriteWatchTableImmediate, sizeof(UINT64)); + *writeWatchTableImmediateWriterHolder.GetRW() = (size_t)g_sw_ww_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } break; @@ -621,14 +627,16 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus if (*(UINT64*)m_pCardTableImmediate != (size_t)g_card_table) { - *(UINT64*)m_pCardTableImmediate = (size_t)g_card_table; + ExecutableWriterHolder cardTableImmediateWriterHolder((UINT64*)m_pCardTableImmediate, sizeof(UINT64)); + *cardTableImmediateWriterHolder.GetRW() = (size_t)g_card_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES if (*(UINT64*)m_pCardBundleTableImmediate != (size_t)g_card_bundle_table) { - *(UINT64*)m_pCardBundleTableImmediate = (size_t)g_card_bundle_table; + ExecutableWriterHolder cardBundleTableImmediateWriterHolder((UINT64*)m_pCardBundleTableImmediate, sizeof(UINT64)); + *cardBundleTableImmediateWriterHolder.GetRW() = (size_t)g_card_bundle_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } #endif diff --git a/src/coreclr/vm/arm/armsinglestepper.cpp b/src/coreclr/vm/arm/armsinglestepper.cpp index 79317263b2223..f9e718ae5420e 100644 --- a/src/coreclr/vm/arm/armsinglestepper.cpp +++ b/src/coreclr/vm/arm/armsinglestepper.cpp @@ -97,11 +97,7 @@ ArmSingleStepper::ArmSingleStepper() ArmSingleStepper::~ArmSingleStepper() { #if !defined(DACCESS_COMPILE) -#ifdef TARGET_UNIX SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(WORD)); -#else - DeleteExecutable(m_rgCode); -#endif #endif } @@ -110,11 +106,7 @@ void ArmSingleStepper::Init() #if !defined(DACCESS_COMPILE) if (m_rgCode == NULL) { -#ifdef TARGET_UNIX m_rgCode = (WORD *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(WORD))); -#else - m_rgCode = new (executable) WORD[kMaxCodeBuffer]; -#endif } #endif } @@ -287,6 +279,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) DWORD idxNextInstruction = 0; + ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); + if (m_originalITState.InITBlock() && !ConditionHolds(pCtx, m_originalITState.CurrentCondition())) { LOG((LF_CORDB, LL_INFO100000, "ArmSingleStepper: Case 1: ITState::Clear;\n")); @@ -295,7 +289,7 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) // to execute. We'll put the correct value back during fixup. ITState::Clear(pCtx); m_fSkipIT = true; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } else if (TryEmulate(pCtx, opcode1, opcode2, false)) { @@ -308,8 +302,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) m_fEmulate = true; // Set breakpoints to stop the execution. This will get us right back here. - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } else { @@ -323,24 +317,24 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) // guarantee one of them will be hit (we don't care which one -- the fixup code will update // the PC and IT state to make it look as though the CPU just executed the current // instruction). - m_rgCode[idxNextInstruction++] = opcode1; + codeWriterHolder.GetRW()[idxNextInstruction++] = opcode1; if (Is32BitInstruction(opcode1)) - m_rgCode[idxNextInstruction++] = opcode2; + codeWriterHolder.GetRW()[idxNextInstruction++] = opcode2; - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } // Always terminate the redirection buffer with a breakpoint. - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; _ASSERTE(idxNextInstruction <= kMaxCodeBuffer); // Set the thread up so it will redirect to our buffer when execution resumes. pCtx->Pc = ((DWORD)(DWORD_PTR)m_rgCode) | THUMB_CODE; // Make sure the CPU sees the updated contents of the buffer. - FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode)); + FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); // Done, set the state. m_state = Applied; diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 930395b56dc7e..3faa8fe36846e 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -978,6 +978,16 @@ g_rgWriteBarrierDescriptors: .global g_rgWriteBarrierDescriptors +// ------------------------------------------------------------------ +// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) + LEAF_ENTRY JIT_WriteBarrier_Callable + + // Branch to the write barrier + ldr r2, =JIT_WriteBarrier_Loc // or R3? See targetarm.h + ldr pc, [r2] + + LEAF_END JIT_WriteBarrier_Callable + #ifdef FEATURE_READYTORUN NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler diff --git a/src/coreclr/vm/arm/asmhelpers.asm b/src/coreclr/vm/arm/asmhelpers.asm index d20540e62090e..82596e66693dc 100644 --- a/src/coreclr/vm/arm/asmhelpers.asm +++ b/src/coreclr/vm/arm/asmhelpers.asm @@ -1724,6 +1724,18 @@ tempReg SETS "$tmpReg" END_WRITE_BARRIERS + IMPORT JIT_WriteBarrier_Loc + +; ------------------------------------------------------------------ +; __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) + LEAF_ENTRY JIT_WriteBarrier_Callable + + ; Branch to the write barrier + ldr r2, =JIT_WriteBarrier_Loc ; or R3? See targetarm.h + ldr pc, [r2] + + LEAF_END + #ifdef FEATURE_READYTORUN NESTED_ENTRY DelayLoad_MethodCall_FakeProlog diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h index 88d0c6802b69d..425c286558432 100644 --- a/src/coreclr/vm/arm/cgencpu.h +++ b/src/coreclr/vm/arm/cgencpu.h @@ -1069,6 +1069,7 @@ struct StubPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -1095,6 +1096,7 @@ struct StubPrecode { return (TADDR)InterlockedCompareExchange( (LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected; } +#endif // !DACCESS_COMPILE #ifdef FEATURE_PREJIT void Fixup(DataImage *image); @@ -1167,6 +1169,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); PCODE GetTarget() @@ -1175,6 +1184,7 @@ struct FixupPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -1201,6 +1211,7 @@ struct FixupPrecode { return (TADDR)InterlockedCompareExchange( (LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected; } +#endif // !DACCESS_COMPILE static BOOL IsFixupPrecodeByASM(PCODE addr) { @@ -1256,6 +1267,7 @@ struct ThisPtrRetBufPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE BOOL SetTargetInterlocked(TADDR target, TADDR expected) { CONTRACTL @@ -1268,6 +1280,7 @@ struct ThisPtrRetBufPrecode { ExecutableWriterHolder precodeWriterHolder(this, sizeof(ThisPtrRetBufPrecode)); return FastInterlockCompareExchange((LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == (LONG)expected; } +#endif // !DACCESS_COMPILE }; typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index aac3e25b18146..6e62df2370338 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -329,16 +329,28 @@ void ComputeWriteBarrierRange(BYTE ** ppbStart, DWORD * pcbLength) { DWORD size = (PBYTE)JIT_PatchedWriteBarrierLast - (PBYTE)JIT_PatchedWriteBarrierStart; *ppbStart = (PBYTE)JIT_PatchedWriteBarrierStart; + if (IsWriteBarrierCopyEnabled()) + { + *ppbStart = GetWriteBarrierCodeLocation(*ppbStart); + } *pcbLength = size; } void CopyWriteBarrier(PCODE dstCode, PCODE srcCode, PCODE endCode) { - TADDR dst = PCODEToPINSTR(dstCode); + TADDR dst = (TADDR)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation((void*)dstCode)); TADDR src = PCODEToPINSTR(srcCode); TADDR end = PCODEToPINSTR(endCode); size_t size = (PBYTE)end - (PBYTE)src; + + ExecutableWriterHolder writeBarrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + writeBarrierWriterHolder = ExecutableWriterHolder((void*)dst, size); + dst = (TADDR)writeBarrierWriterHolder.GetRW(); + } + memcpy((PVOID)dst, (PVOID)src, size); } @@ -419,7 +431,7 @@ void UpdateGCWriteBarriers(bool postGrow = false) } #define GWB_PATCH_OFFSET(_global) \ if (pDesc->m_dw_##_global##_offset != 0xffff) \ - PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset - 1), (UINT32)(dac_cast(_global))); + PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset), (UINT32)(dac_cast(_global))); // Iterate through the write barrier patch table created in the .clrwb section // (see write barrier asm code) @@ -431,6 +443,13 @@ void UpdateGCWriteBarriers(bool postGrow = false) PBYTE to = FindWBMapping(pDesc->m_pFuncStart); if(to) { + to = (PBYTE)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation(to)); + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(to, pDesc->m_pFuncEnd - pDesc->m_pFuncStart); + to = barrierWriterHolder.GetRW(); + } GWB_PATCH_OFFSET(g_lowest_address); GWB_PATCH_OFFSET(g_highest_address); GWB_PATCH_OFFSET(g_ephemeral_low); diff --git a/src/coreclr/vm/arm64/arm64singlestepper.cpp b/src/coreclr/vm/arm64/arm64singlestepper.cpp index d45925311a33e..6c1764647c9f2 100644 --- a/src/coreclr/vm/arm64/arm64singlestepper.cpp +++ b/src/coreclr/vm/arm64/arm64singlestepper.cpp @@ -46,11 +46,7 @@ Arm64SingleStepper::Arm64SingleStepper() Arm64SingleStepper::~Arm64SingleStepper() { #if !defined(DACCESS_COMPILE) -#ifdef TARGET_UNIX SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(uint32_t)); -#else - DeleteExecutable(m_rgCode); -#endif #endif } @@ -59,11 +55,7 @@ void Arm64SingleStepper::Init() #if !defined(DACCESS_COMPILE) if (m_rgCode == NULL) { -#ifdef TARGET_UNIX m_rgCode = (uint32_t *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(uint32_t))); -#else - m_rgCode = new (executable) uint32_t[kMaxCodeBuffer]; -#endif } #endif } @@ -207,7 +199,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx) unsigned int idxNextInstruction = 0; - ExecutableWriterHolder codeWriterHolder(m_rgCode, sizeof(m_rgCode)); + ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); if (TryEmulate(pCtx, opcode, false)) { @@ -230,7 +222,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx) pCtx->Pc = (uint64_t)m_rgCode; // Make sure the CPU sees the updated contents of the buffer. - FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode)); + FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); // Done, set the state. m_state = Applied; diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index e6b47d07b2b0c..8ef66586cd22c 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -270,13 +270,9 @@ LOCAL_LABEL(EphemeralCheckEnabled): ldr x7, [x12] // Update wbs state -#ifdef FEATURE_WRITEBARRIER_COPY PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Table_Loc, x12 ldr x12, [x12] add x12, x12, x9 -#else // FEATURE_WRITEBARRIER_COPY - adr x12, LOCAL_LABEL(wbs_begin) -#endif // FEATURE_WRITEBARRIER_COPY stp x0, x1, [x12], 16 stp x2, x3, [x12], 16 @@ -295,16 +291,10 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT mov x14, x0 // x14 = dst mov x15, x1 // x15 = val -#ifdef FEATURE_WRITEBARRIER_COPY -LOCAL_LABEL(Branch_JIT_WriteBarrier_Copy): // Branch to the write barrier PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Loc, x17 ldr x17, [x17] br x17 -#else // FEATURE_WRITEBARRIER_COPY - // Branch to the write barrier - b C_FUNC(JIT_WriteBarrier) -#endif // FEATURE_WRITEBARRIER_COPY LEAF_END JIT_WriteBarrier_Callable, _TEXT .balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index ffbeb9fd1acb3..17d3a676940bd 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -61,6 +61,10 @@ #ifdef FEATURE_COMINTEROP IMPORT CLRToCOMWorker #endif // FEATURE_COMINTEROP + + IMPORT JIT_WriteBarrier_Table_Loc + IMPORT JIT_WriteBarrier_Loc + TEXTAREA ;; LPVOID __stdcall GetCurrentIP(void); @@ -308,6 +312,7 @@ ThePreStubPatchLabel ; x12 will be used for pointers mov x8, x0 + mov x9, x1 adrp x12, g_card_table ldr x0, [x12, g_card_table] @@ -346,7 +351,9 @@ EphemeralCheckEnabled ldr x7, [x12, g_highest_address] ; Update wbs state - adr x12, wbs_begin + adrp x12, JIT_WriteBarrier_Table_Loc + ldr x12, [x12, JIT_WriteBarrier_Table_Loc] + add x12, x12, x9 stp x0, x1, [x12], 16 stp x2, x3, [x12], 16 stp x4, x5, [x12], 16 @@ -355,9 +362,11 @@ EphemeralCheckEnabled EPILOG_RESTORE_REG_PAIR fp, lr, #16! EPILOG_RETURN + WRITE_BARRIER_END JIT_UpdateWriteBarrierState + ; Begin patchable literal pool ALIGN 64 ; Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line - + WRITE_BARRIER_ENTRY JIT_WriteBarrier_Table wbs_begin wbs_card_table DCQ 0 @@ -375,14 +384,7 @@ wbs_lowest_address DCQ 0 wbs_highest_address DCQ 0 - - WRITE_BARRIER_END JIT_UpdateWriteBarrierState - -; ------------------------------------------------------------------ -; End of the writeable code region - LEAF_ENTRY JIT_PatchedCodeLast - ret lr - LEAF_END + WRITE_BARRIER_END JIT_WriteBarrier_Table ; void JIT_ByRefWriteBarrier ; On entry: @@ -546,6 +548,12 @@ Exit ret lr WRITE_BARRIER_END JIT_WriteBarrier +; ------------------------------------------------------------------ +; End of the writeable code region + LEAF_ENTRY JIT_PatchedCodeLast + ret lr + LEAF_END + #ifdef FEATURE_PREJIT ;------------------------------------------------ ; VirtualMethodFixupStub @@ -1417,9 +1425,10 @@ CallHelper2 mov x14, x0 ; x14 = dst mov x15, x1 ; x15 = val - ; Branch to the write barrier (which is already correctly overwritten with - ; single or multi-proc code based on the current CPU - b JIT_WriteBarrier + ; Branch to the write barrier + adrp x17, JIT_WriteBarrier_Loc + ldr x17, [x17, JIT_WriteBarrier_Loc] + br x17 LEAF_END diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 83e56cfb9f9b9..0641d89ff1a91 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -597,6 +597,7 @@ struct StubPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -623,6 +624,7 @@ struct StubPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE #ifdef FEATURE_PREJIT void Fixup(DataImage *image); @@ -715,6 +717,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); PCODE GetTarget() @@ -723,6 +732,7 @@ struct FixupPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -749,6 +759,7 @@ struct FixupPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE static BOOL IsFixupPrecodeByASM(PCODE addr) { @@ -797,6 +808,7 @@ struct ThisPtrRetBufPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE BOOL SetTargetInterlocked(TADDR target, TADDR expected) { CONTRACTL @@ -810,6 +822,7 @@ struct ThisPtrRetBufPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE }; typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 54cf1c4927548..12d56ddb9867e 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -1067,8 +1067,14 @@ extern "C" void STDCALL JIT_PatchedCodeLast(); static void UpdateWriteBarrierState(bool skipEphemeralCheck) { BYTE *writeBarrierCodeStart = GetWriteBarrierCodeLocation((void*)JIT_PatchedCodeStart); - ExecutableWriterHolder writeBarrierWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart); - JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierWriterHolder.GetRW() - writeBarrierCodeStart); + BYTE *writeBarrierCodeStartRW = writeBarrierCodeStart; + ExecutableWriterHolder writeBarrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + writeBarrierWriterHolder = ExecutableWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart); + writeBarrierCodeStartRW = writeBarrierWriterHolder.GetRW(); + } + JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierCodeStartRW - writeBarrierCodeStart); } void InitJITHelpers1() diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index cdc5925234af9..b60aac924d2e2 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -607,6 +607,11 @@ void EESocketCleanupHelper(bool isExecutingOnAltStack) #endif // TARGET_UNIX #endif // CROSSGEN_COMPILE +void FatalErrorHandler(UINT errorCode, LPCWSTR pszMessage) +{ + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(errorCode, pszMessage); +} + void EEStartupHelper() { CONTRACTL @@ -670,6 +675,8 @@ void EEStartupHelper() // This needs to be done before the EE has started InitializeStartupFlags(); + IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); + ThreadpoolMgr::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); @@ -824,7 +831,7 @@ void EEStartupHelper() g_runtimeLoadedBaseAddress = (SIZE_T)pe.GetBase(); g_runtimeVirtualSize = (SIZE_T)pe.GetVirtualSize(); - InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64)); + ExecutableAllocator::InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64)); } #endif // !TARGET_UNIX diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 02feec829a76b..5c5004f56860a 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -153,7 +153,9 @@ void EEClass::Destruct(MethodTable * pOwningMT) if (pDelegateEEClass->m_pStaticCallStub) { - BOOL fStubDeleted = pDelegateEEClass->m_pStaticCallStub->DecRef(); + ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pStaticCallStub, sizeof(Stub)); + BOOL fStubDeleted = stubWriterHolder.GetRW()->DecRef(); + if (fStubDeleted) { DelegateInvokeStubManager::g_pManager->RemoveStub(pDelegateEEClass->m_pStaticCallStub); @@ -167,7 +169,6 @@ void EEClass::Destruct(MethodTable * pOwningMT) // it is owned by the m_pMulticastStubCache, not by the class // - it is shared across classes. So we don't decrement // its ref count here - delete pDelegateEEClass->m_pUMThunkMarshInfo; } #ifdef FEATURE_COMINTEROP diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 37220786fedda..78721292a3e9f 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -2139,8 +2139,7 @@ VOID EEJitManager::EnsureJumpStubReserve(BYTE * pImageBase, SIZE_T imageSize, SI return; // Unable to allocate the reserve - give up } - pNewReserve->m_ptr = ClrVirtualAllocWithinRange(loAddrCurrent, hiAddrCurrent, - allocChunk, MEM_RESERVE, PAGE_NOACCESS); + pNewReserve->m_ptr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(allocChunk, loAddrCurrent, hiAddrCurrent); if (pNewReserve->m_ptr != NULL) break; @@ -2231,8 +2230,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap if (!pInfo->getThrowOnOutOfMemoryWithinRange() && PEDecoder::GetForceRelocs()) RETURN NULL; #endif - pBaseAddr = ClrVirtualAllocWithinRange(loAddr, hiAddr, - reserveSize, MEM_RESERVE, PAGE_NOACCESS); + pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(reserveSize, loAddr, hiAddr); if (!pBaseAddr) { @@ -2251,7 +2249,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap } else { - pBaseAddr = ClrVirtualAllocExecutable(reserveSize, MEM_RESERVE, PAGE_NOACCESS); + pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(reserveSize); if (!pBaseAddr) ThrowOutOfMemory(); } @@ -2686,15 +2684,14 @@ void EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, size_t reserveFo *pAllocatedSize = sizeof(CodeHeader) + totalSize; -#if defined(HOST_OSX) && defined(HOST_ARM64) -#define FEATURE_WXORX -#endif - -#ifdef FEATURE_WXORX - pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize]; -#else - pCodeHdrRW = pCodeHdr; -#endif + if (ExecutableAllocator::IsWXORXEnabled()) + { + pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize]; + } + else + { + pCodeHdrRW = pCodeHdr; + } #ifdef USE_INDIRECT_CODEHEADER if (requestInfo.IsDynamicDomain()) @@ -3347,7 +3344,7 @@ void EEJitManager::Unload(LoaderAllocator *pAllocator) } } - ResetCodeAllocHint(); + ExecutableAllocator::ResetCodeAllocHint(); } EEJitManager::DomainCodeHeapList::DomainCodeHeapList() diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 8b95dac8cdd77..499880dc16dde 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -3183,12 +3183,11 @@ void ComMethodTable::Cleanup() if (m_pDispatchInfo) delete m_pDispatchInfo; - if (m_pMDescr) - DeleteExecutable(m_pMDescr); if (m_pITypeInfo && !g_fProcessDetach) SafeRelease(m_pITypeInfo); - DeleteExecutable(this); + // The m_pMDescr and the current instance is allocated from the related LoaderAllocator + // so no cleanup is needed here. } @@ -3214,7 +3213,7 @@ void ComMethodTable::LayOutClassMethodTable() SLOT *pComVtable; unsigned cbPrevSlots = 0; unsigned cbAlloc = 0; - NewExecutableHolder pMDMemoryPtr = NULL; + AllocMemHolder pMDMemoryPtr; BYTE* pMethodDescMemory = NULL; size_t writeableOffset = 0; unsigned cbNumParentVirtualMethods = 0; @@ -3321,7 +3320,7 @@ void ComMethodTable::LayOutClassMethodTable() cbAlloc = cbMethodDescs; if (cbAlloc > 0) { - pMDMemoryPtr = (BYTE*) new (executable) BYTE[cbAlloc + sizeof(UINT_PTR)]; + pMDMemoryPtr = m_pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbAlloc + sizeof(UINT_PTR))); pMethodDescMemory = pMDMemoryPtr; methodDescMemoryWriteableHolder = ExecutableWriterHolder(pMethodDescMemory, cbAlloc + sizeof(UINT_PTR)); @@ -3703,7 +3702,6 @@ BOOL ComMethodTable::LayOutInterfaceMethodTable(MethodTable* pClsMT) // Method descs are at the end of the vtable // m_cbSlots interfaces methods + IUnk methods pMethodDescMemory = (BYTE *)&pComVtable[m_cbSlots]; - for (i = 0; i < cbSlots; i++) { ComCallMethodDesc* pNewMD = (ComCallMethodDesc *) (pMethodDescMemory + COMMETHOD_PREPAD); @@ -4495,13 +4493,12 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForClass(MethodTable if (cbToAlloc.IsOverflow()) ThrowHR(COR_E_OVERFLOW); - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()]; + AllocMemHolder pComMT(pClassMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value()))); _ASSERTE(!cbNewSlots.IsOverflow() && !cbTotalSlots.IsOverflow() && !cbVtable.IsOverflow()); ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc.Value()); ComMethodTable* pComMTRW = comMTWriterHolder.GetRW(); - // set up the header pComMTRW->m_ptReserved = (SLOT)(size_t)0xDEADC0FF; // reserved pComMTRW->m_pMT = pClassMT; // pointer to the class method table @@ -4573,7 +4570,7 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForInterface(MethodT if (cbToAlloc.IsOverflow()) ThrowHR(COR_E_OVERFLOW); - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()]; + AllocMemHolder pComMT(pInterfaceMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value()))); _ASSERTE(!cbVtable.IsOverflow() && !cbMethDescs.IsOverflow()); @@ -4639,7 +4636,8 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForBasic(MethodTable unsigned cbVtable = cbExtraSlots * sizeof(SLOT); unsigned cbToAlloc = sizeof(ComMethodTable) + cbVtable; - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc]; + AllocMemHolder pComMT(pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc))); + ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc); ComMethodTable* pComMTRW = comMTWriterHolder.GetRW(); diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h index 2581ddf832fd5..0f1e4b878e4c9 100644 --- a/src/coreclr/vm/comcallablewrapper.h +++ b/src/coreclr/vm/comcallablewrapper.h @@ -499,6 +499,7 @@ struct ComMethodTable // Accessor for the IDispatch information. DispatchInfo* GetDispatchInfo(); +#ifndef DACCESS_COMPILE LONG AddRef() { LIMITED_METHOD_CONTRACT; @@ -527,6 +528,7 @@ struct ComMethodTable return cbRef; } +#endif // DACCESS_COMPILE CorIfaceAttr GetInterfaceType() { @@ -746,6 +748,7 @@ struct ComMethodTable } +#ifndef DACCESS_COMPILE inline REFIID GetIID() { // Cannot use a normal CONTRACT since the return type is ref type which @@ -768,6 +771,7 @@ struct ComMethodTable return m_IID; } +#endif // DACCESS_COMPILE void CheckParentComVisibility(BOOL fForIDispatch) { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b6c17260a1302..1b61e16dec5d3 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1253,7 +1253,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) { GCX_PREEMP(); - pUMThunkMarshInfo = new UMThunkMarshInfo(); + pUMThunkMarshInfo = (UMThunkMarshInfo*)(void*)pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(sizeof(UMThunkMarshInfo))); ExecutableWriterHolder uMThunkMarshInfoWriterHolder(pUMThunkMarshInfo, sizeof(UMThunkMarshInfo)); uMThunkMarshInfoWriterHolder.GetRW()->LoadTimeInit(pInvokeMeth); diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 4a88f81df5210..4f3cf879d10a4 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -41,7 +41,7 @@ class UMEntryThunkFreeList { WRAPPER_NO_CONTRACT; - m_crst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE); + m_crst.Init(CrstUMEntryThunkFreeListLock, CRST_UNSAFE_ANYMODE); } UMEntryThunk *GetUMEntryThunk() diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp index 9dae86aca9377..541d88dc16885 100644 --- a/src/coreclr/vm/dynamicmethod.cpp +++ b/src/coreclr/vm/dynamicmethod.cpp @@ -403,8 +403,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) if (pInfo->m_loAddr != NULL || pInfo->m_hiAddr != NULL) { - m_pBaseAddr = ClrVirtualAllocWithinRange(pInfo->m_loAddr, pInfo->m_hiAddr, - ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); + m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(ReserveBlockSize, pInfo->m_loAddr, pInfo->m_hiAddr); if (!m_pBaseAddr) { if (pInfo->getThrowOnOutOfMemoryWithinRange()) @@ -417,7 +416,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) // top up the ReserveBlockSize to suggested minimum ReserveBlockSize = max(ReserveBlockSize, pInfo->getReserveSize()); - m_pBaseAddr = ClrVirtualAllocExecutable(ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); + m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(ReserveBlockSize); if (!m_pBaseAddr) ThrowOutOfMemory(); } @@ -749,7 +748,7 @@ HostCodeHeap::TrackAllocation* HostCodeHeap::AllocMemory_NoThrow(size_t header, if (m_pLastAvailableCommittedAddr + sizeToCommit <= m_pBaseAddr + m_TotalBytesAvailable) { - if (NULL == ClrVirtualAlloc(m_pLastAvailableCommittedAddr, sizeToCommit, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) + if (NULL == ExecutableAllocator::Instance()->Commit(m_pLastAvailableCommittedAddr, sizeToCommit, true /* isExecutable */)) { LOG((LF_BCL, LL_ERROR, "CodeHeap [0x%p] - VirtualAlloc failed\n", this)); return NULL; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a1fdf255a5ce0..6bf5efcc8028c 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6699,14 +6699,12 @@ AdjustContextForJITHelpers( PCODE ip = GetIP(pContext); -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(ip)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame ip = AdjustWriteBarrierIP(ip); SetIP(pContext, ip); } -#endif // FEATURE_WRITEBARRIER_COPY #ifdef FEATURE_DATABREAKPOINT diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 7fff234ca85ef..4af702fab1499 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -4694,14 +4694,12 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT break; } -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame controlPc = AdjustWriteBarrierIP(controlPc); SetIP(frameContext, controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY UINT_PTR sp = GetSP(frameContext); @@ -5174,13 +5172,11 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e { PCODE controlPc = GetIP(contextRecord); -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location controlPc = AdjustWriteBarrierIP(controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY return g_fEEStarted && ( exceptionRecord->ExceptionCode == STATUS_BREAKPOINT || @@ -5259,14 +5255,12 @@ BOOL HandleHardwareException(PAL_SEHException* ex) { GCX_COOP(); // Must be cooperative to modify frame chain. -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame controlPc = AdjustWriteBarrierIP(controlPc); SetIP(ex->GetContextRecord(), controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY if (IsIPInMarkedJitHelper(controlPc)) { diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index be856dbe1a63a..9ce0cc676f7a7 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1258,9 +1258,9 @@ void RemoveGcCoverageInterrupt(TADDR instrPtr, BYTE * savedInstrPtr, GCCoverageI { ExecutableWriterHolder instrPtrWriterHolder((void*)instrPtr, 4); #ifdef TARGET_ARM - if (GetARMInstructionLength(savedInstrPtr) == 2) + if (GetARMInstructionLength(savedInstrPtr) == 2) *(WORD *)instrPtrWriterHolder.GetRW() = *(WORD *)savedInstrPtr; - else + else *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; #elif defined(TARGET_ARM64) *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; diff --git a/src/coreclr/vm/i386/jithelp.S b/src/coreclr/vm/i386/jithelp.S index facce7cacd3ef..dc56da1d1779e 100644 --- a/src/coreclr/vm/i386/jithelp.S +++ b/src/coreclr/vm/i386/jithelp.S @@ -377,10 +377,27 @@ LEAF_ENTRY JIT_WriteBarrierGroup, _TEXT ret LEAF_END JIT_WriteBarrierGroup, _TEXT -#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS -// ******************************************************************************* -// Write barrier wrappers with fcall calling convention -// + .data + .align 4 + .global C_FUNC(JIT_WriteBarrierEAX_Loc) +C_FUNC(JIT_WriteBarrierEAX_Loc): + .word 0 + .text + +LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT + mov eax, edx + mov edx, ecx + push eax + call 1f +1: + pop eax +2: + add eax, offset _GLOBAL_OFFSET_TABLE_+1 // (2b - 1b) + mov eax, dword ptr [eax + C_FUNC(JIT_WriteBarrierEAX_Loc)@GOT] + xchg eax, dword ptr [esp] + ret +LEAF_END JIT_WriteBarrier_Callable, _TEXT + .macro UniversalWriteBarrierHelper name .align 4 @@ -392,6 +409,11 @@ LEAF_END JIT_\name, _TEXT .endm +#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS +// ******************************************************************************* +// Write barrier wrappers with fcall calling convention +// + // Only define these if we're using the ASM GC write barriers; if this flag is not defined, // we'll use C++ versions of these write barriers. UniversalWriteBarrierHelper CheckedWriteBarrier diff --git a/src/coreclr/vm/i386/jithelp.asm b/src/coreclr/vm/i386/jithelp.asm index 3743ac3cbe02f..3650b3f2afd6d 100644 --- a/src/coreclr/vm/i386/jithelp.asm +++ b/src/coreclr/vm/i386/jithelp.asm @@ -411,15 +411,13 @@ ENDM ;******************************************************************************* ; Write barrier wrappers with fcall calling convention ; -UniversalWriteBarrierHelper MACRO name + + .data ALIGN 4 -PUBLIC @JIT_&name&@8 -@JIT_&name&@8 PROC - mov eax,edx - mov edx,ecx - jmp _JIT_&name&EAX@0 -@JIT_&name&@8 ENDP -ENDM + public _JIT_WriteBarrierEAX_Loc +_JIT_WriteBarrierEAX_Loc dd 0 + + .code ; WriteBarrierStart and WriteBarrierEnd are used to determine bounds of ; WriteBarrier functions so can determine if got AV in them. @@ -429,6 +427,25 @@ _JIT_WriteBarrierGroup@0 PROC ret _JIT_WriteBarrierGroup@0 ENDP + ALIGN 4 +PUBLIC @JIT_WriteBarrier_Callable@8 +@JIT_WriteBarrier_Callable@8 PROC + mov eax,edx + mov edx,ecx + jmp DWORD PTR [_JIT_WriteBarrierEAX_Loc] + +@JIT_WriteBarrier_Callable@8 ENDP + +UniversalWriteBarrierHelper MACRO name + ALIGN 4 +PUBLIC @JIT_&name&@8 +@JIT_&name&@8 PROC + mov eax,edx + mov edx,ecx + jmp _JIT_&name&EAX@0 +@JIT_&name&@8 ENDP +ENDM + ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS ; Only define these if we're using the ASM GC write barriers; if this flag is not defined, ; we'll use C++ versions of these write barriers. @@ -1233,6 +1250,8 @@ fremloopd: ; PatchedCodeStart and PatchedCodeEnd are used to determine bounds of patched code. ; + ALIGN 4 + _JIT_PatchedCodeStart@0 proc public ret _JIT_PatchedCodeStart@0 endp diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 0e366bdbd1a8b..0467f347aaacb 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -1050,10 +1050,18 @@ void InitJITHelpers1() { BYTE * pfunc = (BYTE *) JIT_WriteBarrierReg_PreGrow; - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); int reg = c_rgWriteBarrierRegs[iBarrier]; - memcpy(pBuf, pfunc, 34); + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 34); + pBufRW = barrierWriterHolder.GetRW(); + } + + memcpy(pBufRW, pfunc, 34); // assert the copied code ends in a ret to make sure we got the right length _ASSERTE(pBuf[33] == 0xC3); @@ -1069,24 +1077,24 @@ void InitJITHelpers1() _ASSERTE(pBuf[0] == 0x89); // Update the reg field (bits 3..5) of the ModR/M byte of this instruction - pBuf[1] &= 0xc7; - pBuf[1] |= reg << 3; + pBufRW[1] &= 0xc7; + pBufRW[1] |= reg << 3; // Second instruction to patch is cmp reg, imm32 (low bound) _ASSERTE(pBuf[2] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[3] &= 0xf8; - pBuf[3] |= reg; + pBufRW[3] &= 0xf8; + pBufRW[3] |= reg; #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization just jump to the old one // Use the slow one from time to time in a debug build because // there are some good asserts in the unoptimized one if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK) || DEBUG_RANDOM_BARRIER_CHECK) { - pfunc = &pBuf[0]; + pfunc = &pBufRW[0]; *pfunc++ = 0xE9; // JMP c_rgDebugWriteBarriers[iBarrier] - *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (pfunc + sizeof(DWORD)); + *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (&pBuf[1] + sizeof(DWORD)); } #endif // WRITE_BARRIER_CHECK } @@ -1132,7 +1140,7 @@ void ValidateWriteBarrierHelpers() #endif // WRITE_BARRIER_CHECK // first validate the PreGrow helper - BYTE* pWriteBarrierFunc = reinterpret_cast(JIT_WriteBarrierEAX); + BYTE* pWriteBarrierFunc = GetWriteBarrierCodeLocation(reinterpret_cast(JIT_WriteBarrierEAX)); // ephemeral region DWORD* pLocation = reinterpret_cast(&pWriteBarrierFunc[AnyGrow_EphemeralLowerBound]); @@ -1170,7 +1178,7 @@ void ValidateWriteBarrierHelpers() #endif //CODECOVERAGE /*********************************************************************/ -#define WriteBarrierIsPreGrow() (((BYTE *)JIT_WriteBarrierEAX)[10] == 0xc1) +#define WriteBarrierIsPreGrow() ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[10] == 0xc1) /*********************************************************************/ @@ -1188,20 +1196,28 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization if we are checking write barrier - if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier + if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier return stompWBCompleteActions; #endif // WRITE_BARRIER_CHECK // Update the lower bound. for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++) { - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); + + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 42); + pBufRW = barrierWriterHolder.GetRW(); + } // assert there is in fact a cmp r/m32, imm32 there _ASSERTE(pBuf[2] == 0x81); // Update the immediate which is the lower bound of the ephemeral generation - size_t *pfunc = (size_t *) &pBuf[AnyGrow_EphemeralLowerBound]; + size_t *pfunc = (size_t *) &pBufRW[AnyGrow_EphemeralLowerBound]; //avoid trivial self modifying code if (*pfunc != (size_t) g_ephemeral_low) { @@ -1214,7 +1230,7 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) _ASSERTE(pBuf[10] == 0x81); // Update the upper bound if we are using the PostGrow thunk. - pfunc = (size_t *) &pBuf[PostGrow_EphemeralUpperBound]; + pfunc = (size_t *) &pBufRW[PostGrow_EphemeralUpperBound]; //avoid trivial self modifying code if (*pfunc != (size_t) g_ephemeral_high) { @@ -1244,7 +1260,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization if we are checking write barrier - if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier + if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier return stompWBCompleteActions; #endif // WRITE_BARRIER_CHECK @@ -1253,12 +1269,20 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++) { - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); int reg = c_rgWriteBarrierRegs[iBarrier]; size_t *pfunc; - // Check if we are still using the pre-grow version of the write barrier. + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 42); + pBufRW = barrierWriterHolder.GetRW(); + } + + // Check if we are still using the pre-grow version of the write barrier. if (bWriteBarrierIsPreGrow) { // Check if we need to use the upper bounds checking barrier stub. @@ -1271,7 +1295,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) } pfunc = (size_t *) JIT_WriteBarrierReg_PostGrow; - memcpy(pBuf, pfunc, 42); + memcpy(pBufRW, pfunc, 42); // assert the copied code ends in a ret to make sure we got the right length _ASSERTE(pBuf[41] == 0xC3); @@ -1287,35 +1311,35 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) _ASSERTE(pBuf[0] == 0x89); // Update the reg field (bits 3..5) of the ModR/M byte of this instruction - pBuf[1] &= 0xc7; - pBuf[1] |= reg << 3; + pBufRW[1] &= 0xc7; + pBufRW[1] |= reg << 3; // Second instruction to patch is cmp reg, imm32 (low bound) _ASSERTE(pBuf[2] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[3] &= 0xf8; - pBuf[3] |= reg; + pBufRW[3] &= 0xf8; + pBufRW[3] |= reg; // Third instruction to patch is another cmp reg, imm32 (high bound) _ASSERTE(pBuf[10] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[11] &= 0xf8; - pBuf[11] |= reg; + pBufRW[11] &= 0xf8; + pBufRW[11] |= reg; bStompWriteBarrierEphemeral = true; // What we're trying to update is the offset field of a // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[22] == 0x80); - pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[34] == 0xC6); - pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation]; } else @@ -1324,14 +1348,14 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[14] == 0x80); - pfunc = (size_t *) &pBuf[PreGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PreGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[26] == 0xC6); - pfunc = (size_t *) &pBuf[PreGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PreGrow_CardTableSecondLocation]; } } else @@ -1340,13 +1364,13 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[22] == 0x80); - pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[34] == 0xC6); - pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation]; } // Stick in the adjustment value. diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index 61c5dfd90cbfc..564363053fc6a 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -4829,7 +4829,7 @@ VOID StubLinkerCPU::EmitArrayOpStub(const ArrayOpScript* pArrayOpScript) X86EmitOp(0x8d, kEDX, elemBaseReg, elemOfs, elemScaledReg, elemScale); // call JIT_Writeable_Thunks_Buf.WriteBarrierReg[0] (== EAX) - X86EmitCall(NewExternalCodeLabel((LPVOID) &JIT_WriteBarrierEAX), 0); + X86EmitCall(NewExternalCodeLabel((LPVOID) GetWriteBarrierCodeLocation(&JIT_WriteBarrierEAX)), 0); } else #else // TARGET_AMD64 diff --git a/src/coreclr/vm/i386/stublinkerx86.h b/src/coreclr/vm/i386/stublinkerx86.h index af5244d077193..564c999975e7c 100644 --- a/src/coreclr/vm/i386/stublinkerx86.h +++ b/src/coreclr/vm/i386/stublinkerx86.h @@ -536,7 +536,7 @@ struct StubPrecode { return rel32Decode(PTR_HOST_MEMBER_TADDR(StubPrecode, this, m_rel32)); } - +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -562,6 +562,7 @@ struct StubPrecode { ExecutableWriterHolder rel32Holder(&m_rel32, 4); return rel32SetInterlocked(&m_rel32, rel32Holder.GetRW(), target, expected, (MethodDesc*)GetMethodDesc()); } +#endif // !DACCESS_COMPILE }; IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_movR10) == OFFSETOF_PRECODE_TYPE);) IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_type) == OFFSETOF_PRECODE_TYPE_MOV_R10);) @@ -646,6 +647,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); #else // HAS_FIXUP_PRECODE_CHUNKS TADDR GetMethodDesc() diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a1e4d93d881de..882e2c29cef04 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11875,7 +11875,7 @@ WORD CEEJitInfo::getRelocTypeHint(void * target) if (m_fAllowRel32) { // The JIT calls this method for data addresses only. It always uses REL32s for direct code targets. - if (IsPreferredExecutableRange(target)) + if (ExecutableAllocator::IsPreferredExecutableRange(target)) return IMAGE_REL_BASED_REL32; } #endif // TARGET_AMD64 diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index ca9d03c2141d3..e071d0717d179 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -238,15 +238,10 @@ extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj); -#if defined(TARGET_ARM64) || defined(FEATURE_WRITEBARRIER_COPY) // ARM64 JIT_WriteBarrier uses speciall ABI and thus is not callable directly // Copied write barriers must be called at a different location extern "C" FCDECL2(VOID, JIT_WriteBarrier_Callable, Object **dst, Object *ref); #define WriteBarrier_Helper JIT_WriteBarrier_Callable -#else -// in other cases the regular JIT helper is callable. -#define WriteBarrier_Helper JIT_WriteBarrier -#endif extern "C" FCDECL1(void, JIT_InternalThrow, unsigned exceptNum); extern "C" FCDECL1(void*, JIT_InternalThrowFromHelper, unsigned exceptNum); @@ -344,28 +339,25 @@ EXTERN_C FCDECL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift); #ifdef TARGET_X86 +#define ENUM_X86_WRITE_BARRIER_REGISTERS() \ + X86_WRITE_BARRIER_REGISTER(EAX) \ + X86_WRITE_BARRIER_REGISTER(ECX) \ + X86_WRITE_BARRIER_REGISTER(EBX) \ + X86_WRITE_BARRIER_REGISTER(ESI) \ + X86_WRITE_BARRIER_REGISTER(EDI) \ + X86_WRITE_BARRIER_REGISTER(EBP) + extern "C" { - void STDCALL JIT_CheckedWriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEBP(); // JIThelp.asm/JIThelp.s - - void STDCALL JIT_DebugWriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEBP(); // JIThelp.asm/JIThelp.s - - void STDCALL JIT_WriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEBP(); // JIThelp.asm/JIThelp.s + +// JIThelp.asm/JIThelp.s +#define X86_WRITE_BARRIER_REGISTER(reg) \ + void STDCALL JIT_CheckedWriteBarrier##reg(); \ + void STDCALL JIT_DebugWriteBarrier##reg(); \ + void STDCALL JIT_WriteBarrier##reg(); + + ENUM_X86_WRITE_BARRIER_REGISTERS() +#undef X86_WRITE_BARRIER_REGISTER void STDCALL JIT_WriteBarrierGroup(); void STDCALL JIT_WriteBarrierGroup_End(); diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 4f222be4a2c03..0a77e4445f06f 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1137,7 +1137,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) _ASSERTE(dwTotalReserveMemSize <= VIRTUAL_ALLOC_RESERVE_GRANULARITY); #endif - BYTE * initReservedMem = ClrVirtualAllocExecutable(dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS); + BYTE * initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize); m_InitialReservedMemForLoaderHeaps = initReservedMem; @@ -1672,18 +1672,25 @@ void AssemblyLoaderAllocator::SetCollectible() { CONTRACTL { - THROWS; + NOTHROW; } CONTRACTL_END; m_IsCollectible = true; -#ifndef DACCESS_COMPILE - m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); -#endif } #ifndef DACCESS_COMPILE +void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain) +{ + m_Id.Init(); + LoaderAllocator::Init((BaseDomain *)pAppDomain); + if (IsCollectible()) + { + m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); + } +} + #ifndef CROSSGEN_COMPILE AssemblyLoaderAllocator::~AssemblyLoaderAllocator() diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl index a826675ccc93c..993732d4010f8 100644 --- a/src/coreclr/vm/loaderallocator.inl +++ b/src/coreclr/vm/loaderallocator.inl @@ -21,12 +21,6 @@ inline void GlobalLoaderAllocator::Init(BaseDomain *pDomain) LoaderAllocator::Init(pDomain, m_ExecutableHeapInstance); } -inline void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain) -{ - m_Id.Init(); - LoaderAllocator::Init((BaseDomain *)pAppDomain); -} - inline BOOL LoaderAllocatorID::Equals(LoaderAllocatorID *pId) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index bd3984d8697cd..db308ab208a8e 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -4188,46 +4188,6 @@ c_CentralJumpCode = { }; #include -#elif defined(TARGET_AMD64) - -#include -static const struct CentralJumpCode { - BYTE m_movzxRAX[4]; - BYTE m_shlEAX[4]; - BYTE m_movRAX[2]; - MethodDesc* m_pBaseMD; - BYTE m_addR10RAX[3]; - BYTE m_jmp[1]; - INT32 m_rel32; - - inline void Setup(CentralJumpCode* pCodeRX, MethodDesc* pMD, PCODE target, LoaderAllocator *pLoaderAllocator) { - WRAPPER_NO_CONTRACT; - m_pBaseMD = pMD; - m_rel32 = rel32UsingJumpStub(&pCodeRX->m_rel32, target, pMD, pLoaderAllocator); - } - - inline BOOL CheckTarget(TADDR target) { - WRAPPER_NO_CONTRACT; - TADDR addr = rel32Decode(PTR_HOST_MEMBER_TADDR(CentralJumpCode, this, m_rel32)); - if (*PTR_BYTE(addr) == 0x48 && - *PTR_BYTE(addr+1) == 0xB8 && - *PTR_BYTE(addr+10) == 0xFF && - *PTR_BYTE(addr+11) == 0xE0) - { - addr = *PTR_TADDR(addr+2); - } - return (addr == target); - } -} -c_CentralJumpCode = { - { 0x48, 0x0F, 0xB6, 0xC0 }, // movzx rax,al - { 0x48, 0xC1, 0xE0, MethodDesc::ALIGNMENT_SHIFT }, // shl rax, MethodDesc::ALIGNMENT_SHIFT - { 0x49, 0xBA }, NULL, // mov r10, pBaseMD - { 0x4C, 0x03, 0xD0 }, // add r10,rax - { 0xE9 }, 0 // jmp PreStub -}; -#include - #elif defined(TARGET_ARM) #include diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index 80731c191e737..0bd2bd657f9ad 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -480,7 +480,9 @@ void Precode::Reset() #ifdef HAS_FIXUP_PRECODE_CHUNKS if (t == PRECODE_FIXUP) { - size = sizeof(FixupPrecode) + sizeof(PTR_MethodDesc); + // The writeable size the Init method accesses is dynamic depending on + // the FixupPrecode members. + size = ((FixupPrecode*)this)->GetSizeRW(); } else #endif diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 0971334af4d31..e61802b984950 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -713,14 +713,12 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext) // get our caller's PSP, or our caller's caller's SP. while (!ExecutionManager::IsManagedCode(uControlPc)) { -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(uControlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame uControlPc = AdjustWriteBarrierIP(uControlPc); SetIP(pContext, uControlPc); } -#endif // FEATURE_WRITEBARRIER_COPY #ifndef TARGET_UNIX uControlPc = VirtualUnwindCallFrame(pContext); diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 04a33e3982613..304cb4fb35b44 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -846,7 +846,7 @@ Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags) ); ASSERT(pStub != NULL); - bool fSuccess = EmitStub(pStub, globalsize, pHeap); + bool fSuccess = EmitStub(pStub, globalsize, size, pHeap); #ifdef STUBLINKER_GENERATES_UNWIND_INFO if (fSuccess) @@ -1007,13 +1007,13 @@ int StubLinker::CalculateSize(int* pGlobalSize) return globalsize + datasize; } -bool StubLinker::EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap) +bool StubLinker::EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap) { STANDARD_VM_CONTRACT; BYTE *pCode = (BYTE*)(pStub->GetBlob()); - ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub)); + ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub) + totalSize); Stub *pStubRW = stubWriterHolder.GetRW(); BYTE *pCodeRW = (BYTE*)(pStubRW->GetBlob()); @@ -2013,11 +2013,7 @@ VOID Stub::DeleteStub() FillMemory(this+1, m_numCodeBytes, 0xcc); #endif -#ifndef TARGET_UNIX - DeleteExecutable((BYTE*)GetAllocationBase()); -#else delete [] (BYTE*)GetAllocationBase(); -#endif } } @@ -2124,11 +2120,7 @@ Stub* Stub::NewStub(PTR_VOID pCode, DWORD flags) BYTE *pBlock; if (pHeap == NULL) { -#ifndef TARGET_UNIX - pBlock = new (executable) BYTE[totalSize]; -#else pBlock = new BYTE[totalSize]; -#endif } else { diff --git a/src/coreclr/vm/stublink.h b/src/coreclr/vm/stublink.h index 94326f9962ea7..9613fd48f687d 100644 --- a/src/coreclr/vm/stublink.h +++ b/src/coreclr/vm/stublink.h @@ -395,7 +395,7 @@ class StubLinker // Writes out the code element into memory following the // stub object. - bool EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap); + bool EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap); CodeRun *GetLastCodeRunIfAny(); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index fa93110399d39..2c55f8770b01b 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1078,18 +1078,30 @@ DWORD_PTR Thread::OBJREF_HASH = OBJREF_TABSIZE; extern "C" void STDCALL JIT_PatchedCodeStart(); extern "C" void STDCALL JIT_PatchedCodeLast(); -#ifdef FEATURE_WRITEBARRIER_COPY - static void* s_barrierCopy = NULL; BYTE* GetWriteBarrierCodeLocation(VOID* barrier) { - return (BYTE*)s_barrierCopy + ((BYTE*)barrier - (BYTE*)JIT_PatchedCodeStart); + if (IsWriteBarrierCopyEnabled()) + { + return (BYTE*)PINSTRToPCODE((TADDR)s_barrierCopy + ((TADDR)barrier - (TADDR)JIT_PatchedCodeStart)); + } + else + { + return (BYTE*)barrier; + } } BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc) { - return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart))); + if (IsWriteBarrierCopyEnabled()) + { + return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart))); + } + else + { + return FALSE; + } } PCODE AdjustWriteBarrierIP(PCODE controlPc) @@ -1100,14 +1112,21 @@ PCODE AdjustWriteBarrierIP(PCODE controlPc) return (PCODE)JIT_PatchedCodeStart + (controlPc - (PCODE)s_barrierCopy); } +#ifdef TARGET_X86 +extern "C" void *JIT_WriteBarrierEAX_Loc; +#else extern "C" void *JIT_WriteBarrier_Loc; +#endif + #ifdef TARGET_ARM64 extern "C" void (*JIT_WriteBarrier_Table)(); extern "C" void *JIT_WriteBarrier_Loc = 0; extern "C" void *JIT_WriteBarrier_Table_Loc = 0; #endif // TARGET_ARM64 -#endif // FEATURE_WRITEBARRIER_COPY +#ifdef TARGET_ARM +extern "C" void *JIT_WriteBarrier_Loc = 0; +#endif // TARGET_ARM #ifndef TARGET_UNIX // g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out. @@ -1138,50 +1157,80 @@ void InitThreadManager() _ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart > (ptrdiff_t)0); _ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart < (ptrdiff_t)GetOsPageSize()); -#ifdef FEATURE_WRITEBARRIER_COPY - s_barrierCopy = ClrVirtualAlloc(NULL, g_SystemInfo.dwAllocationGranularity, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (s_barrierCopy == NULL) + if (IsWriteBarrierCopyEnabled()) { - _ASSERTE(!"ClrVirtualAlloc of GC barrier code page failed"); - COMPlusThrowWin32(); - } + s_barrierCopy = ExecutableAllocator::Instance()->Reserve(g_SystemInfo.dwAllocationGranularity); + ExecutableAllocator::Instance()->Commit(s_barrierCopy, g_SystemInfo.dwAllocationGranularity, true); + if (s_barrierCopy == NULL) + { + _ASSERTE(!"Allocation of GC barrier code page failed"); + COMPlusThrowWin32(); + } - { - size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; - ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize); - memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize); - } + { + size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; + ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize); + memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize); + } - // Store the JIT_WriteBarrier copy location to a global variable so that helpers - // can jump to it. - JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); + // Store the JIT_WriteBarrier copy location to a global variable so that helpers + // can jump to it. +#ifdef TARGET_X86 + JIT_WriteBarrierEAX_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrierEAX); - SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); +#define X86_WRITE_BARRIER_REGISTER(reg) \ + SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF_##reg, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg)); \ + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("@WriteBarrier" #reg)); -#ifdef TARGET_ARM64 - // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. - JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table); + ENUM_X86_WRITE_BARRIER_REGISTERS() - SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); - SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); -#endif // TARGET_ARM64 +#undef X86_WRITE_BARRIER_REGISTER -#else // FEATURE_WRITEBARRIER_COPY +#else // TARGET_X86 + JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); +#endif // TARGET_X86 + SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier")); - // I am using virtual protect to cover the entire range that this code falls in. - // +#ifdef TARGET_ARM64 + // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. + JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table); +#endif // TARGET_ARM64 - // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth, - // so instead we'll leave it writable from here forward. +#if defined(TARGET_ARM64) || defined(TARGET_ARM) + SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier")); + SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier")); +#endif // TARGET_ARM64 || TARGET_ARM - DWORD oldProt; - if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart, - PAGE_EXECUTE_READWRITE, &oldProt)) + } + else { - _ASSERTE(!"ClrVirtualProtect of code page failed"); - COMPlusThrowWin32(); + // I am using virtual protect to cover the entire range that this code falls in. + // + + // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth, + // so instead we'll leave it writable from here forward. + + DWORD oldProt; + if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart, + PAGE_EXECUTE_READWRITE, &oldProt)) + { + _ASSERTE(!"ClrVirtualProtect of code page failed"); + COMPlusThrowWin32(); + } + +#ifdef TARGET_X86 + JIT_WriteBarrierEAX_Loc = (void*)JIT_WriteBarrierEAX; +#else + JIT_WriteBarrier_Loc = (void*)JIT_WriteBarrier; +#endif +#ifdef TARGET_ARM64 + // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. + JIT_WriteBarrier_Table_Loc = (void*)&JIT_WriteBarrier_Table; +#endif // TARGET_ARM64 } -#endif // FEATURE_WRITEBARRIER_COPY #ifndef TARGET_UNIX _ASSERTE(GetThreadNULLOk() == NULL); diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index d18b21d58f95a..7d600dab5edac 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -6271,18 +6271,23 @@ class ThreadStateNCStackHolder BOOL Debug_IsLockedViaThreadSuspension(); -#ifdef FEATURE_WRITEBARRIER_COPY +inline BOOL IsWriteBarrierCopyEnabled() +{ +#ifdef DACCESS_COMPILE + return FALSE; +#else // DACCESS_COMPILE +#ifdef HOST_OSX + return TRUE; +#else + return ExecutableAllocator::IsWXORXEnabled(); +#endif +#endif // DACCESS_COMPILE +} BYTE* GetWriteBarrierCodeLocation(VOID* barrier); BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc); PCODE AdjustWriteBarrierIP(PCODE controlPc); -#else // FEATURE_WRITEBARRIER_COPY - -#define GetWriteBarrierCodeLocation(barrier) ((BYTE*)(barrier)) - -#endif // FEATURE_WRITEBARRIER_COPY - #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) extern thread_local Thread* t_pStackWalkerWalkingThread; #define SET_THREAD_TYPE_STACKWALKER(pThread) t_pStackWalkerWalkingThread = pThread diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 95d568d641c73..3af4c52afc9bb 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -641,7 +641,7 @@ void VirtualCallStubManager::Init(BaseDomain *pDomain, LoaderAllocator *pLoaderA dwTotalReserveMemSize); } - initReservedMem = ClrVirtualAllocExecutable (dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS); + initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize); m_initialReservedMemForHeaps = (BYTE *) initReservedMem; @@ -2766,11 +2766,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad } #endif - ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder) -#ifdef TARGET_AMD64 - + sizeof(DispatchStubShort) -#endif - ); + ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize); dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode, addrOfFail, (size_t)pMTExpected @@ -2833,9 +2829,9 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE } CONTRACT_END; //allocate from the requisite heap and copy the template over it. - DispatchHolder * holder = (DispatchHolder*) (void*) - dispatch_heap->AllocAlignedMem(DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG), CODE_SIZE_ALIGN); - ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder) + sizeof(DispatchStubLong)); + size_t dispatchHolderSize = DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG); + DispatchHolder * holder = (DispatchHolder*) (void*)dispatch_heap->AllocAlignedMem(dispatchHolderSize, CODE_SIZE_ALIGN); + ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize); dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode, addrOfFail, From bb3982239e11d559f839aba445ea7d698084e623 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 11 Jul 2021 08:24:08 -0700 Subject: [PATCH 70/72] Adding more tests for the generic math feature (#55377) * Fixing some issues in the generic math implementation * Adding generic math tests for integer types --- .../System.Private.CoreLib/src/System/Byte.cs | 12 +- .../System.Private.CoreLib/src/System/Char.cs | 12 +- .../src/System/Decimal.cs | 8 +- .../src/System/Double.cs | 8 +- .../System.Private.CoreLib/src/System/Half.cs | 8 +- .../src/System/IInteger.cs | 4 +- .../src/System/Int16.cs | 18 +- .../src/System/Int32.cs | 8 +- .../src/System/Int64.cs | 16 +- .../src/System/IntPtr.cs | 38 +- .../src/System/SByte.cs | 18 +- .../src/System/Single.cs | 8 +- .../src/System/UInt16.cs | 12 +- .../src/System/UInt32.cs | 16 +- .../src/System/UInt64.cs | 16 +- .../src/System/UIntPtr.cs | 42 +- .../System.Runtime/ref/System.Runtime.cs | 44 +- .../tests/System.Runtime.Tests.csproj | 22 +- .../tests/System/ByteTests.GenericMath.cs | 1175 +++++++++++ .../tests/System/CharTests.GenericMath.cs | 1057 ++++++++++ .../tests/System/GenericMathHelpers.cs | 228 +++ .../tests/System/GenericMathTests.cs | 73 - .../tests/System/Int16Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/Int32GenericMathTests.cs | 35 - .../tests/System/Int32Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/Int64Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/IntPtrTests.GenericMath.cs | 1746 +++++++++++++++++ .../tests/System/SByteTests.GenericMath.cs | 1181 +++++++++++ .../tests/System/UInt16Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UInt32Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UInt64Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UIntPtrTests.GenericMath.cs | 1695 ++++++++++++++++ 32 files changed, 14313 insertions(+), 255 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs delete mode 100644 src/libraries/System.Runtime/tests/System/GenericMathTests.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs delete mode 100644 src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 39bfeae2a7e44..3d5b448159a63 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -313,11 +313,11 @@ static byte IBinaryInteger.PopCount(byte value) => (byte)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) + static byte IBinaryInteger.RotateLeft(byte value, int rotateAmount) => (byte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] - static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) + static byte IBinaryInteger.RotateRight(byte value, int rotateAmount) => (byte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] @@ -382,11 +382,11 @@ static byte IBinaryNumber.Log2(byte value) [RequiresPreviewFeatures] static byte IDecrementOperators.operator --(byte value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked byte IDecrementOperators.operator --(byte value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -418,11 +418,11 @@ static byte IBinaryNumber.Log2(byte value) [RequiresPreviewFeatures] static byte IIncrementOperators.operator ++(byte value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked byte IIncrementOperators.operator ++(byte value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 2f58b54b7460a..26ff3399fe10e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1093,11 +1093,11 @@ static char IBinaryInteger.PopCount(char value) => (char)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static char IBinaryInteger.RotateLeft(char value, char rotateAmount) + static char IBinaryInteger.RotateLeft(char value, int rotateAmount) => (char)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static char IBinaryInteger.RotateRight(char value, char rotateAmount) + static char IBinaryInteger.RotateRight(char value, int rotateAmount) => (char)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] @@ -1162,11 +1162,11 @@ static char IBinaryNumber.Log2(char value) [RequiresPreviewFeatures] static char IDecrementOperators.operator --(char value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked char IDecrementOperators.operator --(char value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -1198,11 +1198,11 @@ static char IBinaryNumber.Log2(char value) [RequiresPreviewFeatures] static char IIncrementOperators.operator ++(char value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked char IIncrementOperators.operator ++(char value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index e5f26f2ff0148..5c4f30795cd6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -1126,11 +1126,11 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [RequiresPreviewFeatures] static decimal IDecrementOperators.operator --(decimal value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked decimal IDecrementOperators.operator --(decimal value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -1162,11 +1162,11 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [RequiresPreviewFeatures] static decimal IIncrementOperators.operator ++(decimal value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked decimal IIncrementOperators.operator ++(decimal value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 85d5f8c36f11d..10178510f6332 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -550,11 +550,11 @@ static double IBinaryNumber.Log2(double value) [RequiresPreviewFeatures] static double IDecrementOperators.operator --(double value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked double IDecrementOperators.operator --(double value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -828,11 +828,11 @@ static double IFloatingPoint.Truncate(double x) [RequiresPreviewFeatures] static double IIncrementOperators.operator ++(double value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked double IIncrementOperators.operator ++(double value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index ea64c10f83538..c70cb712bfe63 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -802,7 +802,7 @@ static Half IBinaryNumber.Log2(Half value) static Half IDecrementOperators.operator --(Half value) { var tmp = (float)value; - tmp--; + --tmp; return (Half)tmp; } @@ -810,7 +810,7 @@ static Half IBinaryNumber.Log2(Half value) // static checked Half IDecrementOperators.operator --(Half value) // { // var tmp = (float)value; - // tmp--; + // --tmp; // return (Half)tmp; // } @@ -1132,7 +1132,7 @@ static Half IFloatingPoint.Truncate(Half x) static Half IIncrementOperators.operator ++(Half value) { var tmp = (float)value; - tmp++; + ++tmp; return (Half)tmp; } @@ -1140,7 +1140,7 @@ static Half IFloatingPoint.Truncate(Half x) // static checked Half IIncrementOperators.operator ++(Half value) // { // var tmp = (float)value; - // tmp++; + // ++tmp; // return (Half)tmp; // } diff --git a/src/libraries/System.Private.CoreLib/src/System/IInteger.cs b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs index a93b439625e98..89ec036bb147a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs @@ -31,13 +31,13 @@ public interface IBinaryInteger /// The value which is rotated left by . /// The amount by which is rotated left. /// The result of rotating left by . - static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateLeft(TSelf value, int rotateAmount); /// Rotates a value right by a given amount. /// The value which is rotated right by . /// The amount by which is rotated right. /// The result of rotating right by . - static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateRight(TSelf value, int rotateAmount); /// Computes the number of trailing zeros in a value. /// The value whose trailing zeroes are to be counted. diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index f42c84de2bd87..909b37f77b340 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -316,12 +316,12 @@ static short IBinaryInteger.PopCount(short value) => (short)BitOperations.PopCount((ushort)value); [RequiresPreviewFeatures] - static short IBinaryInteger.RotateLeft(short value, short rotateAmount) - => (short)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); + static short IBinaryInteger.RotateLeft(short value, int rotateAmount) + => (short)((value << (rotateAmount & 15)) | ((ushort)value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static short IBinaryInteger.RotateRight(short value, short rotateAmount) - => (short)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); + static short IBinaryInteger.RotateRight(short value, int rotateAmount) + => (short)(((ushort)value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] static short IBinaryInteger.TrailingZeroCount(short value) @@ -391,11 +391,11 @@ static short IBinaryNumber.Log2(short value) [RequiresPreviewFeatures] static short IDecrementOperators.operator --(short value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked short IDecrementOperators.operator --(short value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -427,11 +427,11 @@ static short IBinaryNumber.Log2(short value) [RequiresPreviewFeatures] static short IIncrementOperators.operator ++(short value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked short IIncrementOperators.operator ++(short value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -565,7 +565,7 @@ static short INumber.CreateSaturating(TOther value) { if (typeof(TOther) == typeof(byte)) { - return (short)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index d8514bc89f7e7..68c6a70cdb043 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -383,11 +383,11 @@ static int IBinaryNumber.Log2(int value) [RequiresPreviewFeatures] static int IDecrementOperators.operator --(int value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked int IDecrementOperators.operator --(int value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -419,11 +419,11 @@ static int IBinaryNumber.Log2(int value) [RequiresPreviewFeatures] static int IIncrementOperators.operator ++(int value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked int IIncrementOperators.operator ++(int value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index c0420f5052e5b..1646cf96457ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -295,12 +295,12 @@ static long IBinaryInteger.PopCount(long value) => BitOperations.PopCount((ulong)value); [RequiresPreviewFeatures] - static long IBinaryInteger.RotateLeft(long value, long rotateAmount) - => (long)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + static long IBinaryInteger.RotateLeft(long value, int rotateAmount) + => (long)BitOperations.RotateLeft((ulong)value, rotateAmount); [RequiresPreviewFeatures] - static long IBinaryInteger.RotateRight(long value, long rotateAmount) - => (long)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + static long IBinaryInteger.RotateRight(long value, int rotateAmount) + => (long)BitOperations.RotateRight((ulong)value, rotateAmount); [RequiresPreviewFeatures] static long IBinaryInteger.TrailingZeroCount(long value) @@ -370,11 +370,11 @@ static long IBinaryNumber.Log2(long value) [RequiresPreviewFeatures] static long IDecrementOperators.operator --(long value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked long IDecrementOperators.operator --(long value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -406,11 +406,11 @@ static long IBinaryNumber.Log2(long value) [RequiresPreviewFeatures] static long IIncrementOperators.operator ++(long value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked long IIncrementOperators.operator ++(long value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index f64effb167adf..a9592617b2eb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -278,11 +278,11 @@ static nint IBinaryInteger.LeadingZeroCount(nint value) { if (Environment.Is64BitProcess) { - return BitOperations.LeadingZeroCount((uint)value); + return BitOperations.LeadingZeroCount((ulong)value); } else { - return BitOperations.LeadingZeroCount((ulong)value); + return BitOperations.LeadingZeroCount((uint)value); } } @@ -291,38 +291,38 @@ static nint IBinaryInteger.PopCount(nint value) { if (Environment.Is64BitProcess) { - return BitOperations.PopCount((uint)value); + return BitOperations.PopCount((ulong)value); } else { - return BitOperations.PopCount((ulong)value); + return BitOperations.PopCount((uint)value); } } [RequiresPreviewFeatures] - static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) + static nint IBinaryInteger.RotateLeft(nint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); + return (nint)BitOperations.RotateLeft((ulong)value, rotateAmount); } else { - return (nint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + return (nint)BitOperations.RotateLeft((uint)value, rotateAmount); } } [RequiresPreviewFeatures] - static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) + static nint IBinaryInteger.RotateRight(nint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + return (nint)BitOperations.RotateRight((ulong)value, rotateAmount); } else { - return (nint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + return (nint)BitOperations.RotateRight((uint)value, rotateAmount); } } @@ -331,11 +331,11 @@ static nint IBinaryInteger.TrailingZeroCount(nint value) { if (Environment.Is64BitProcess) { - return BitOperations.TrailingZeroCount((uint)value); + return BitOperations.TrailingZeroCount((ulong)value); } else { - return BitOperations.TrailingZeroCount((ulong)value); + return BitOperations.TrailingZeroCount((uint)value); } } @@ -357,11 +357,11 @@ static nint IBinaryNumber.Log2(nint value) if (Environment.Is64BitProcess) { - return BitOperations.Log2((uint)value); + return BitOperations.Log2((ulong)value); } else { - return BitOperations.Log2((ulong)value); + return BitOperations.Log2((uint)value); } } @@ -411,11 +411,11 @@ static nint IBinaryNumber.Log2(nint value) [RequiresPreviewFeatures] static nint IDecrementOperators.operator --(nint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked nint IDecrementOperators.operator --(nint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -447,11 +447,11 @@ static nint IBinaryNumber.Log2(nint value) [RequiresPreviewFeatures] static nint IIncrementOperators.operator ++(nint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked nint IIncrementOperators.operator ++(nint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -585,7 +585,7 @@ static nint INumber.CreateSaturating(TOther value) { if (typeof(TOther) == typeof(byte)) { - return (nint)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index cec0716214ffb..ce811d0cffd93 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -318,19 +318,19 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [RequiresPreviewFeatures] static sbyte IBinaryInteger.LeadingZeroCount(sbyte value) - => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 16); + => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 24); [RequiresPreviewFeatures] static sbyte IBinaryInteger.PopCount(sbyte value) => (sbyte)BitOperations.PopCount((byte)value); [RequiresPreviewFeatures] - static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) - => (sbyte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); + static sbyte IBinaryInteger.RotateLeft(sbyte value, int rotateAmount) + => (sbyte)((value << (rotateAmount & 7)) | ((byte)value >> ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] - static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) - => (sbyte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); + static sbyte IBinaryInteger.RotateRight(sbyte value, int rotateAmount) + => (sbyte)(((byte)value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) @@ -400,11 +400,11 @@ static sbyte IBinaryNumber.Log2(sbyte value) [RequiresPreviewFeatures] static sbyte IDecrementOperators.operator --(sbyte value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked sbyte IDecrementOperators.operator --(sbyte value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -436,11 +436,11 @@ static sbyte IBinaryNumber.Log2(sbyte value) [RequiresPreviewFeatures] static sbyte IIncrementOperators.operator ++(sbyte value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked sbyte IIncrementOperators.operator ++(sbyte value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 92dc3a9b6dfb5..59dbddd253cf2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -542,11 +542,11 @@ static float IBinaryNumber.Log2(float value) [RequiresPreviewFeatures] static float IDecrementOperators.operator --(float value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked float IDecrementOperators.operator --(float value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -820,11 +820,11 @@ static float IFloatingPoint.Truncate(float x) [RequiresPreviewFeatures] static float IIncrementOperators.operator ++(float value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked float IIncrementOperators.operator ++(float value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 218c092ed1927..49c08cc99fab3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -307,11 +307,11 @@ static ushort IBinaryInteger.PopCount(ushort value) => (ushort)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) + static ushort IBinaryInteger.RotateLeft(ushort value, int rotateAmount) => (ushort)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) + static ushort IBinaryInteger.RotateRight(ushort value, int rotateAmount) => (ushort)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] @@ -376,11 +376,11 @@ static ushort IBinaryNumber.Log2(ushort value) [RequiresPreviewFeatures] static ushort IDecrementOperators.operator --(ushort value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked ushort IDecrementOperators.operator --(ushort value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -412,11 +412,11 @@ static ushort IBinaryNumber.Log2(ushort value) [RequiresPreviewFeatures] static ushort IIncrementOperators.operator ++(ushort value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked ushort IIncrementOperators.operator ++(ushort value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 14d06e9b0eff2..70f073b1e15bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -293,12 +293,12 @@ static uint IBinaryInteger.PopCount(uint value) => (uint)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) - => BitOperations.RotateLeft(value, (int)rotateAmount); + static uint IBinaryInteger.RotateLeft(uint value, int rotateAmount) + => BitOperations.RotateLeft(value, rotateAmount); [RequiresPreviewFeatures] - static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) - => BitOperations.RotateRight(value, (int)rotateAmount); + static uint IBinaryInteger.RotateRight(uint value, int rotateAmount) + => BitOperations.RotateRight(value, rotateAmount); [RequiresPreviewFeatures] static uint IBinaryInteger.TrailingZeroCount(uint value) @@ -362,11 +362,11 @@ static uint IBinaryNumber.Log2(uint value) [RequiresPreviewFeatures] static uint IDecrementOperators.operator --(uint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked uint IDecrementOperators.operator --(uint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -398,11 +398,11 @@ static uint IBinaryNumber.Log2(uint value) [RequiresPreviewFeatures] static uint IIncrementOperators.operator ++(uint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked uint IIncrementOperators.operator ++(uint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index e341b0bee93a0..6b2a45f1a8ebe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -292,12 +292,12 @@ static ulong IBinaryInteger.PopCount(ulong value) => (ulong)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) - => BitOperations.RotateLeft(value, (int)rotateAmount); + static ulong IBinaryInteger.RotateLeft(ulong value, int rotateAmount) + => BitOperations.RotateLeft(value, rotateAmount); [RequiresPreviewFeatures] - static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) - => BitOperations.RotateRight(value, (int)rotateAmount); + static ulong IBinaryInteger.RotateRight(ulong value, int rotateAmount) + => BitOperations.RotateRight(value, rotateAmount); [RequiresPreviewFeatures] static ulong IBinaryInteger.TrailingZeroCount(ulong value) @@ -361,11 +361,11 @@ static ulong IBinaryNumber.Log2(ulong value) [RequiresPreviewFeatures] static ulong IDecrementOperators.operator --(ulong value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked ulong IDecrementOperators.operator --(ulong value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -397,11 +397,11 @@ static ulong IBinaryNumber.Log2(ulong value) [RequiresPreviewFeatures] static ulong IIncrementOperators.operator ++(ulong value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked ulong IIncrementOperators.operator ++(ulong value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 43c549fe94e39..b413125474f55 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -270,11 +270,11 @@ static nuint IBinaryInteger.LeadingZeroCount(nuint value) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.LeadingZeroCount((uint)value); + return (nuint)BitOperations.LeadingZeroCount((ulong)value); } else { - return (nuint)BitOperations.LeadingZeroCount((ulong)value); + return (nuint)BitOperations.LeadingZeroCount((uint)value); } } @@ -283,37 +283,37 @@ static nuint IBinaryInteger.PopCount(nuint value) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.PopCount((uint)value); + return (nuint)BitOperations.PopCount((ulong)value); } else { - return (nuint)BitOperations.PopCount((ulong)value); + return (nuint)BitOperations.PopCount((uint)value); } } [RequiresPreviewFeatures] - static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) + static nuint IBinaryInteger.RotateLeft(nuint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); + return (nuint)BitOperations.RotateLeft((ulong)value, rotateAmount); } else { - return (nuint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + return (nuint)BitOperations.RotateLeft((uint)value, rotateAmount); } } [RequiresPreviewFeatures] - static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) + static nuint IBinaryInteger.RotateRight(nuint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + return (nuint)BitOperations.RotateRight((ulong)value, rotateAmount); } else { - return (nuint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + return (nuint)BitOperations.RotateRight((uint)value, rotateAmount); } } @@ -322,11 +322,11 @@ static nuint IBinaryInteger.TrailingZeroCount(nuint value) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.TrailingZeroCount((uint)value); + return (nuint)BitOperations.TrailingZeroCount((ulong)value); } else { - return (nuint)BitOperations.TrailingZeroCount((ulong)value); + return (nuint)BitOperations.TrailingZeroCount((uint)value); } } @@ -339,11 +339,11 @@ static bool IBinaryNumber.IsPow2(nuint value) { if (Environment.Is64BitProcess) { - return BitOperations.IsPow2((uint)value); + return BitOperations.IsPow2((ulong)value); } else { - return BitOperations.IsPow2((ulong)value); + return BitOperations.IsPow2((uint)value); } } @@ -352,11 +352,11 @@ static nuint IBinaryNumber.Log2(nuint value) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.Log2((uint)value); + return (nuint)BitOperations.Log2((ulong)value); } else { - return (nuint)BitOperations.Log2((ulong)value); + return (nuint)BitOperations.Log2((uint)value); } } @@ -406,11 +406,11 @@ static nuint IBinaryNumber.Log2(nuint value) [RequiresPreviewFeatures] static nuint IDecrementOperators.operator --(nuint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked nuint IDecrementOperators.operator --(nuint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -442,11 +442,11 @@ static nuint IBinaryNumber.Log2(nuint value) [RequiresPreviewFeatures] static nuint IIncrementOperators.operator ++(nuint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked nuint IIncrementOperators.operator ++(nuint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -661,7 +661,7 @@ static nuint INumber.CreateTruncating(TOther value) { if (typeof(TOther) == typeof(byte)) { - return (nuint)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 1b9b1d241b854..67e80443421df 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -777,9 +777,9 @@ public static void SetByte(System.Array array, int index, byte value) { } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static byte IBinaryInteger.PopCount(byte value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) { throw null; } + static byte IBinaryInteger.RotateLeft(byte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) { throw null; } + static byte IBinaryInteger.RotateRight(byte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static byte IBinaryInteger.TrailingZeroCount(byte value) { throw null; } @@ -997,9 +997,9 @@ public CannotUnloadAppDomainException(string? message, System.Exception? innerEx [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static char IBinaryInteger.PopCount(char value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static char IBinaryInteger.RotateLeft(char value, char rotateAmount) { throw null; } + static char IBinaryInteger.RotateLeft(char value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static char IBinaryInteger.RotateRight(char value, char rotateAmount) { throw null; } + static char IBinaryInteger.RotateRight(char value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static char IBinaryInteger.TrailingZeroCount(char value) { throw null; } @@ -3379,8 +3379,8 @@ public partial interface IBinaryInteger : System.IBinaryNumber, Sy { static abstract TSelf LeadingZeroCount(TSelf value); static abstract TSelf PopCount(TSelf value); - static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); - static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateLeft(TSelf value, int rotateAmount); + static abstract TSelf RotateRight(TSelf value, int rotateAmount); static abstract TSelf TrailingZeroCount(TSelf value); } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] @@ -3760,9 +3760,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static short IBinaryInteger.PopCount(short value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static short IBinaryInteger.RotateLeft(short value, short rotateAmount) { throw null; } + static short IBinaryInteger.RotateLeft(short value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static short IBinaryInteger.RotateRight(short value, short rotateAmount) { throw null; } + static short IBinaryInteger.RotateRight(short value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static short IBinaryInteger.TrailingZeroCount(short value) { throw null; } @@ -4118,9 +4118,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static long IBinaryInteger.PopCount(long value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static long IBinaryInteger.RotateLeft(long value, long rotateAmount) { throw null; } + static long IBinaryInteger.RotateLeft(long value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static long IBinaryInteger.RotateRight(long value, long rotateAmount) { throw null; } + static long IBinaryInteger.RotateRight(long value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static long IBinaryInteger.TrailingZeroCount(long value) { throw null; } @@ -4306,9 +4306,9 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nint IBinaryInteger.PopCount(nint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) { throw null; } + static nint IBinaryInteger.RotateLeft(nint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) { throw null; } + static nint IBinaryInteger.RotateRight(nint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nint IBinaryInteger.TrailingZeroCount(nint value) { throw null; } @@ -5269,9 +5269,9 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static sbyte IBinaryInteger.PopCount(sbyte value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) { throw null; } + static sbyte IBinaryInteger.RotateLeft(sbyte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) { throw null; } + static sbyte IBinaryInteger.RotateRight(sbyte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) { throw null; } @@ -6979,9 +6979,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ushort IBinaryInteger.PopCount(ushort value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) { throw null; } + static ushort IBinaryInteger.RotateLeft(ushort value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) { throw null; } + static ushort IBinaryInteger.RotateRight(ushort value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ushort IBinaryInteger.TrailingZeroCount(ushort value) { throw null; } @@ -7156,9 +7156,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static uint IBinaryInteger.PopCount(uint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) { throw null; } + static uint IBinaryInteger.RotateLeft(uint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) { throw null; } + static uint IBinaryInteger.RotateRight(uint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static uint IBinaryInteger.TrailingZeroCount(uint value) { throw null; } @@ -7333,9 +7333,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ulong IBinaryInteger.PopCount(ulong value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) { throw null; } + static ulong IBinaryInteger.RotateLeft(ulong value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) { throw null; } + static ulong IBinaryInteger.RotateRight(ulong value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ulong IBinaryInteger.TrailingZeroCount(ulong value) { throw null; } @@ -7515,9 +7515,9 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nuint IBinaryInteger.PopCount(nuint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) { throw null; } + static nuint IBinaryInteger.RotateLeft(nuint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) { throw null; } + static nuint IBinaryInteger.RotateRight(nuint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nuint IBinaryInteger.TrailingZeroCount(nuint value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 32bbac1e99304..0f4092248ae8e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -1,12 +1,16 @@ - + true + true $(NoWarn),1718,SYSLIB0013 true true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser disable + + $(DefineConstants);FEATURE_GENERIC_MATH + @@ -81,7 +85,6 @@ - @@ -89,7 +92,6 @@ - @@ -247,6 +249,20 @@ + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs new file mode 100644 index 0000000000000..140279d54f2d6 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class ByteTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((byte)0x00, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((byte)0x00, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((byte)0xFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((byte)0x01, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((byte)0x01, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((byte)0x00, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((byte)0x01, AdditionOperatorsHelper.op_Addition((byte)0x00, (byte)1)); + Assert.Equal((byte)0x02, AdditionOperatorsHelper.op_Addition((byte)0x01, (byte)1)); + Assert.Equal((byte)0x80, AdditionOperatorsHelper.op_Addition((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, AdditionOperatorsHelper.op_Addition((byte)0x80, (byte)1)); + Assert.Equal((byte)0x00, AdditionOperatorsHelper.op_Addition((byte)0xFF, (byte)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((byte)0x08, BinaryIntegerHelper.LeadingZeroCount((byte)0x00)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.LeadingZeroCount((byte)0x01)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.LeadingZeroCount((byte)0x7F)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.LeadingZeroCount((byte)0x80)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.LeadingZeroCount((byte)0xFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.PopCount((byte)0x00)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.PopCount((byte)0x01)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.PopCount((byte)0x7F)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.PopCount((byte)0x80)); + Assert.Equal((byte)0x08, BinaryIntegerHelper.PopCount((byte)0xFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.RotateLeft((byte)0x00, 1)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.RotateLeft((byte)0x01, 1)); + Assert.Equal((byte)0xFE, BinaryIntegerHelper.RotateLeft((byte)0x7F, 1)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.RotateLeft((byte)0x80, 1)); + Assert.Equal((byte)0xFF, BinaryIntegerHelper.RotateLeft((byte)0xFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.RotateRight((byte)0x00, 1)); + Assert.Equal((byte)0x80, BinaryIntegerHelper.RotateRight((byte)0x01, 1)); + Assert.Equal((byte)0xBF, BinaryIntegerHelper.RotateRight((byte)0x7F, 1)); + Assert.Equal((byte)0x40, BinaryIntegerHelper.RotateRight((byte)0x80, 1)); + Assert.Equal((byte)0xFF, BinaryIntegerHelper.RotateRight((byte)0xFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((byte)0x08, BinaryIntegerHelper.TrailingZeroCount((byte)0x00)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0x01)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0x7F)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.TrailingZeroCount((byte)0x80)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0xFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((byte)0x00)); + Assert.True(BinaryNumberHelper.IsPow2((byte)0x01)); + Assert.False(BinaryNumberHelper.IsPow2((byte)0x7F)); + Assert.True(BinaryNumberHelper.IsPow2((byte)0x80)); + Assert.False(BinaryNumberHelper.IsPow2((byte)0xFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((byte)0x00, BinaryNumberHelper.Log2((byte)0x00)); + Assert.Equal((byte)0x00, BinaryNumberHelper.Log2((byte)0x01)); + Assert.Equal((byte)0x06, BinaryNumberHelper.Log2((byte)0x7F)); + Assert.Equal((byte)0x07, BinaryNumberHelper.Log2((byte)0x80)); + Assert.Equal((byte)0x07, BinaryNumberHelper.Log2((byte)0xFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x01, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x80, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7F, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFF, BitwiseOperatorsHelper.op_BitwiseOr((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x00, (byte)1)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7E, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFE, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((byte)0xFF, BitwiseOperatorsHelper.op_OnesComplement((byte)0x00)); + Assert.Equal((byte)0xFE, BitwiseOperatorsHelper.op_OnesComplement((byte)0x01)); + Assert.Equal((byte)0x80, BitwiseOperatorsHelper.op_OnesComplement((byte)0x7F)); + Assert.Equal((byte)0x7F, BitwiseOperatorsHelper.op_OnesComplement((byte)0x80)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_OnesComplement((byte)0xFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((byte)0x00, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x01, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x7F, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x80, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x00, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x01, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x7F, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x80, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((byte)0x00, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((byte)0x01, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0x7F, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0x80, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x00, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x01, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x7F, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x80, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((byte)0xFF, DecrementOperatorsHelper.op_Decrement((byte)0x00)); + Assert.Equal((byte)0x00, DecrementOperatorsHelper.op_Decrement((byte)0x01)); + Assert.Equal((byte)0x7E, DecrementOperatorsHelper.op_Decrement((byte)0x7F)); + Assert.Equal((byte)0x7F, DecrementOperatorsHelper.op_Decrement((byte)0x80)); + Assert.Equal((byte)0xFE, DecrementOperatorsHelper.op_Decrement((byte)0xFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((byte)0x00, DivisionOperatorsHelper.op_Division((byte)0x00, (byte)2)); + Assert.Equal((byte)0x00, DivisionOperatorsHelper.op_Division((byte)0x01, (byte)2)); + Assert.Equal((byte)0x3F, DivisionOperatorsHelper.op_Division((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x40, DivisionOperatorsHelper.op_Division((byte)0x80, (byte)2)); + Assert.Equal((byte)0x7F, DivisionOperatorsHelper.op_Division((byte)0xFF, (byte)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x00, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((byte)0x01, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x7F, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x80, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x00, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((byte)0x01, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x7F, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x80, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((byte)0x01, IncrementOperatorsHelper.op_Increment((byte)0x00)); + Assert.Equal((byte)0x02, IncrementOperatorsHelper.op_Increment((byte)0x01)); + Assert.Equal((byte)0x80, IncrementOperatorsHelper.op_Increment((byte)0x7F)); + Assert.Equal((byte)0x81, IncrementOperatorsHelper.op_Increment((byte)0x80)); + Assert.Equal((byte)0x00, IncrementOperatorsHelper.op_Increment((byte)0xFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((byte)0x00, ModulusOperatorsHelper.op_Modulus((byte)0x00, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0x01, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x00, ModulusOperatorsHelper.op_Modulus((byte)0x80, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0xFF, (byte)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((byte)0x00, MultiplyOperatorsHelper.op_Multiply((byte)0x00, (byte)2)); + Assert.Equal((byte)0x02, MultiplyOperatorsHelper.op_Multiply((byte)0x01, (byte)2)); + Assert.Equal((byte)0xFE, MultiplyOperatorsHelper.op_Multiply((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x00, MultiplyOperatorsHelper.op_Multiply((byte)0x80, (byte)2)); + Assert.Equal((byte)0xFE, MultiplyOperatorsHelper.op_Multiply((byte)0xFF, (byte)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((byte)0x00, NumberHelper.Abs((byte)0x00)); + Assert.Equal((byte)0x01, NumberHelper.Abs((byte)0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Abs((byte)0x7F)); + Assert.Equal((byte)0x80, NumberHelper.Abs((byte)0x80)); + Assert.Equal((byte)0xFF, NumberHelper.Abs((byte)0xFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((byte)0x01, NumberHelper.Clamp((byte)0x00, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x01, NumberHelper.Clamp((byte)0x01, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0x7F, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0x80, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0xFF, (byte)0x01, (byte)0x3F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Create(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.Create(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create((char)0x0001)); + Assert.Throws(() => NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((byte)0x00, (byte)0x00), NumberHelper.DivRem((byte)0x00, (byte)2)); + Assert.Equal(((byte)0x00, (byte)0x01), NumberHelper.DivRem((byte)0x01, (byte)2)); + Assert.Equal(((byte)0x3F, (byte)0x01), NumberHelper.DivRem((byte)0x7F, (byte)2)); + Assert.Equal(((byte)0x40, (byte)0x00), NumberHelper.DivRem((byte)0x80, (byte)2)); + Assert.Equal(((byte)0x7F, (byte)0x01), NumberHelper.DivRem((byte)0xFF, (byte)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((byte)0x01, NumberHelper.Max((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Max((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7F, NumberHelper.Max((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x80, NumberHelper.Max((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFF, NumberHelper.Max((byte)0xFF, (byte)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((byte)0x00, NumberHelper.Min((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x01, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x80, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0xFF, (byte)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((byte)0x00, NumberHelper.Sign((byte)0x00)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x01)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x7F)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x80)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0xFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((byte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((byte)0x7F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((byte)0x80, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((byte)0xFF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + byte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((byte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((byte)0x7F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + byte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_LeftShift((byte)0x00, 1)); + Assert.Equal((byte)0x02, ShiftOperatorsHelper.op_LeftShift((byte)0x01, 1)); + Assert.Equal((byte)0xFE, ShiftOperatorsHelper.op_LeftShift((byte)0x7F, 1)); + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_LeftShift((byte)0x80, 1)); + Assert.Equal((byte)0xFE, ShiftOperatorsHelper.op_LeftShift((byte)0xFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_RightShift((byte)0x00, 1)); + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_RightShift((byte)0x01, 1)); + Assert.Equal((byte)0x3F, ShiftOperatorsHelper.op_RightShift((byte)0x7F, 1)); + Assert.Equal((byte)0x40, ShiftOperatorsHelper.op_RightShift((byte)0x80, 1)); + Assert.Equal((byte)0x7F, ShiftOperatorsHelper.op_RightShift((byte)0xFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((byte)0xFF, SubtractionOperatorsHelper.op_Subtraction((byte)0x00, (byte)1)); + Assert.Equal((byte)0x00, SubtractionOperatorsHelper.op_Subtraction((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7E, SubtractionOperatorsHelper.op_Subtraction((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x7F, SubtractionOperatorsHelper.op_Subtraction((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFE, SubtractionOperatorsHelper.op_Subtraction((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((byte)0x00, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x00)); + Assert.Equal((byte)0xFF, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x01)); + Assert.Equal((byte)0x81, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x7F)); + Assert.Equal((byte)0x80, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x80)); + Assert.Equal((byte)0x01, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0xFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((byte)0x00, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x00)); + Assert.Equal((byte)0x01, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x01)); + Assert.Equal((byte)0x7F, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x7F)); + Assert.Equal((byte)0x80, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x80)); + Assert.Equal((byte)0xFF, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0xFF)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Valid_TestData), MemberType = typeof(ByteTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, byte expected) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Invalid_TestData), MemberType = typeof(ByteTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(ByteTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, byte expected) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Invalid_TestData), MemberType = typeof(ByteTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(byte), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(byte), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs new file mode 100644 index 0000000000000..7dd512f9871ed --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs @@ -0,0 +1,1057 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class CharTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((char)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((char)0x0000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((char)0xFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((char)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((char)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((char)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((char)0x0001, AdditionOperatorsHelper.op_Addition((char)0x0000, (char)1)); + Assert.Equal((char)0x0002, AdditionOperatorsHelper.op_Addition((char)0x0001, (char)1)); + Assert.Equal((char)0x8000, AdditionOperatorsHelper.op_Addition((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, AdditionOperatorsHelper.op_Addition((char)0x8000, (char)1)); + Assert.Equal((char)0x0000, AdditionOperatorsHelper.op_Addition((char)0xFFFF, (char)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((char)0x0010, BinaryIntegerHelper.LeadingZeroCount((char)0x0000)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.LeadingZeroCount((char)0x0001)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.LeadingZeroCount((char)0x7FFF)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.LeadingZeroCount((char)0x8000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.LeadingZeroCount((char)0xFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.PopCount((char)0x0000)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.PopCount((char)0x0001)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.PopCount((char)0x7FFF)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.PopCount((char)0x8000)); + Assert.Equal((char)0x0010, BinaryIntegerHelper.PopCount((char)0xFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.RotateLeft((char)0x0000, 1)); + Assert.Equal((char)0x0002, BinaryIntegerHelper.RotateLeft((char)0x0001, 1)); + Assert.Equal((char)0xFFFE, BinaryIntegerHelper.RotateLeft((char)0x7FFF, 1)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.RotateLeft((char)0x8000, 1)); + Assert.Equal((char)0xFFFF, BinaryIntegerHelper.RotateLeft((char)0xFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.RotateRight((char)0x0000, 1)); + Assert.Equal((char)0x8000, BinaryIntegerHelper.RotateRight((char)0x0001, 1)); + Assert.Equal((char)0xBFFF, BinaryIntegerHelper.RotateRight((char)0x7FFF, 1)); + Assert.Equal((char)0x4000, BinaryIntegerHelper.RotateRight((char)0x8000, 1)); + Assert.Equal((char)0xFFFF, BinaryIntegerHelper.RotateRight((char)0xFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((char)0x0010, BinaryIntegerHelper.TrailingZeroCount((char)0x0000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0x0001)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0x7FFF)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.TrailingZeroCount((char)0x8000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0xFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((char)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((char)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((char)0x7FFF)); + Assert.True(BinaryNumberHelper.IsPow2((char)0x8000)); + Assert.False(BinaryNumberHelper.IsPow2((char)0xFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((char)0x0000, BinaryNumberHelper.Log2((char)0x0000)); + Assert.Equal((char)0x0000, BinaryNumberHelper.Log2((char)0x0001)); + Assert.Equal((char)0x000E, BinaryNumberHelper.Log2((char)0x7FFF)); + Assert.Equal((char)0x000F, BinaryNumberHelper.Log2((char)0x8000)); + Assert.Equal((char)0x000F, BinaryNumberHelper.Log2((char)0xFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x0001, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x7FFF, (char)1)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x8000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFF, BitwiseOperatorsHelper.op_BitwiseOr((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x0000, (char)1)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((char)0xFFFF, BitwiseOperatorsHelper.op_OnesComplement((char)0x0000)); + Assert.Equal((char)0xFFFE, BitwiseOperatorsHelper.op_OnesComplement((char)0x0001)); + Assert.Equal((char)0x8000, BitwiseOperatorsHelper.op_OnesComplement((char)0x7FFF)); + Assert.Equal((char)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement((char)0x8000)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_OnesComplement((char)0xFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((char)0x0000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x0001, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x7FFF, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x8000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x0000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x0001, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x7FFF, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x8000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((char)0x0000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((char)0x0001, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0x7FFF, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0x8000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x0000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x0001, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x7FFF, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x8000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((char)0xFFFF, DecrementOperatorsHelper.op_Decrement((char)0x0000)); + Assert.Equal((char)0x0000, DecrementOperatorsHelper.op_Decrement((char)0x0001)); + Assert.Equal((char)0x7FFE, DecrementOperatorsHelper.op_Decrement((char)0x7FFF)); + Assert.Equal((char)0x7FFF, DecrementOperatorsHelper.op_Decrement((char)0x8000)); + Assert.Equal((char)0xFFFE, DecrementOperatorsHelper.op_Decrement((char)0xFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((char)0x0000, DivisionOperatorsHelper.op_Division((char)0x0000, (char)2)); + Assert.Equal((char)0x0000, DivisionOperatorsHelper.op_Division((char)0x0001, (char)2)); + Assert.Equal((char)0x3FFF, DivisionOperatorsHelper.op_Division((char)0x7FFF, (char)2)); + Assert.Equal((char)0x4000, DivisionOperatorsHelper.op_Division((char)0x8000, (char)2)); + Assert.Equal((char)0x7FFF, DivisionOperatorsHelper.op_Division((char)0xFFFF, (char)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x0000, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((char)0x0001, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x7FFF, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x8000, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x0000, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((char)0x0001, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x7FFF, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x8000, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((char)0x0001, IncrementOperatorsHelper.op_Increment((char)0x0000)); + Assert.Equal((char)0x0002, IncrementOperatorsHelper.op_Increment((char)0x0001)); + Assert.Equal((char)0x8000, IncrementOperatorsHelper.op_Increment((char)0x7FFF)); + Assert.Equal((char)0x8001, IncrementOperatorsHelper.op_Increment((char)0x8000)); + Assert.Equal((char)0x0000, IncrementOperatorsHelper.op_Increment((char)0xFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((char)0x0000, ModulusOperatorsHelper.op_Modulus((char)0x0000, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0x0001, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0x7FFF, (char)2)); + Assert.Equal((char)0x0000, ModulusOperatorsHelper.op_Modulus((char)0x8000, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0xFFFF, (char)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((char)0x0000, MultiplyOperatorsHelper.op_Multiply((char)0x0000, (char)2)); + Assert.Equal((char)0x0002, MultiplyOperatorsHelper.op_Multiply((char)0x0001, (char)2)); + Assert.Equal((char)0xFFFE, MultiplyOperatorsHelper.op_Multiply((char)0x7FFF, (char)2)); + Assert.Equal((char)0x0000, MultiplyOperatorsHelper.op_Multiply((char)0x8000, (char)2)); + Assert.Equal((char)0xFFFE, MultiplyOperatorsHelper.op_Multiply((char)0xFFFF, (char)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((char)0x0000, NumberHelper.Abs((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Abs((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Abs((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Abs((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Abs((char)0xFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((char)0x0001, NumberHelper.Clamp((char)0x0000, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x0001, NumberHelper.Clamp((char)0x0001, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0x7FFF, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0x8000, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0xFFFF, (char)0x0001, (char)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((char)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Create((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((char)0x007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Create(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((char)0xFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((char)0x0000, (char)0x0000), NumberHelper.DivRem((char)0x0000, (char)2)); + Assert.Equal(((char)0x0000, (char)0x0001), NumberHelper.DivRem((char)0x0001, (char)2)); + Assert.Equal(((char)0x3FFF, (char)0x0001), NumberHelper.DivRem((char)0x7FFF, (char)2)); + Assert.Equal(((char)0x4000, (char)0x0000), NumberHelper.DivRem((char)0x8000, (char)2)); + Assert.Equal(((char)0x7FFF, (char)0x0001), NumberHelper.DivRem((char)0xFFFF, (char)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((char)0x0001, NumberHelper.Max((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Max((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFF, NumberHelper.Max((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8000, NumberHelper.Max((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFF, NumberHelper.Max((char)0xFFFF, (char)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((char)0x0000, NumberHelper.Min((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x0001, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x7FFF, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x8000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0xFFFF, (char)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((char)0x0000, NumberHelper.Sign((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x0001)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x7FFF)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x8000)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0xFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((char)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((char)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((char)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + char result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((char)0x8000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((char)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + char result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((char)0x007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((char)0x8000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((char)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + char result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_LeftShift((char)0x0000, 1)); + Assert.Equal((char)0x0002, ShiftOperatorsHelper.op_LeftShift((char)0x0001, 1)); + Assert.Equal((char)0xFFFE, ShiftOperatorsHelper.op_LeftShift((char)0x7FFF, 1)); + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_LeftShift((char)0x8000, 1)); + Assert.Equal((char)0xFFFE, ShiftOperatorsHelper.op_LeftShift((char)0xFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_RightShift((char)0x0000, 1)); + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_RightShift((char)0x0001, 1)); + Assert.Equal((char)0x3FFF, ShiftOperatorsHelper.op_RightShift((char)0x7FFF, 1)); + Assert.Equal((char)0x4000, ShiftOperatorsHelper.op_RightShift((char)0x8000, 1)); + Assert.Equal((char)0x7FFF, ShiftOperatorsHelper.op_RightShift((char)0xFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((char)0xFFFF, SubtractionOperatorsHelper.op_Subtraction((char)0x0000, (char)1)); + Assert.Equal((char)0x0000, SubtractionOperatorsHelper.op_Subtraction((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((char)0x7FFF, (char)1)); + Assert.Equal((char)0x7FFF, SubtractionOperatorsHelper.op_Subtraction((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFE, SubtractionOperatorsHelper.op_Subtraction((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((char)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x0000)); + Assert.Equal((char)0xFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x0001)); + Assert.Equal((char)0x8001, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x7FFF)); + Assert.Equal((char)0x8000, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x8000)); + Assert.Equal((char)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0xFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((char)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x0000)); + Assert.Equal((char)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x0001)); + Assert.Equal((char)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x7FFF)); + Assert.Equal((char)0x8000, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x8000)); + Assert.Equal((char)0xFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0xFFFF)); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs b/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs new file mode 100644 index 0000000000000..2895dd8841962 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs @@ -0,0 +1,228 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; + +namespace System.Tests +{ + [RequiresPreviewFeatures] + public static class AdditionOperatorsHelper + where TSelf : IAdditionOperators + { + public static TResult op_Addition(TSelf left, TOther right) => left + right; + } + + [RequiresPreviewFeatures] + public static class AdditiveIdentityHelper + where TSelf : IAdditiveIdentity + { + public static TResult AdditiveIdentity => TSelf.AdditiveIdentity; + } + + [RequiresPreviewFeatures] + public static class BinaryIntegerHelper + where TSelf : IBinaryInteger + { + public static TSelf LeadingZeroCount(TSelf value) => TSelf.LeadingZeroCount(value); + + public static TSelf PopCount(TSelf value) => TSelf.PopCount(value); + + public static TSelf RotateLeft(TSelf value, int rotateAmount) => TSelf.RotateLeft(value, rotateAmount); + + public static TSelf RotateRight(TSelf value, int rotateAmount) => TSelf.RotateRight(value, rotateAmount); + + public static TSelf TrailingZeroCount(TSelf value) => TSelf.TrailingZeroCount(value); + } + + [RequiresPreviewFeatures] + public static class BinaryNumberHelper + where TSelf : IBinaryNumber + { + public static bool IsPow2(TSelf value) => TSelf.IsPow2(value); + + public static TSelf Log2(TSelf value) => TSelf.Log2(value); + } + + [RequiresPreviewFeatures] + public static class BitwiseOperatorsHelper + where TSelf : IBitwiseOperators + { + public static TResult op_BitwiseAnd(TSelf left, TOther right) => left & right; + + public static TResult op_BitwiseOr(TSelf left, TOther right) => left | right; + + public static TResult op_ExclusiveOr(TSelf left, TOther right) => left ^ right; + + public static TResult op_OnesComplement(TSelf value) => ~value; + } + + [RequiresPreviewFeatures] + public static class ComparisonOperatorsHelper + where TSelf : IComparisonOperators + { + public static bool op_GreaterThan(TSelf left, TOther right) => left > right; + + public static bool op_GreaterThanOrEqual(TSelf left, TOther right) => left >= right; + + public static bool op_LessThan(TSelf left, TOther right) => left < right; + + public static bool op_LessThanOrEqual(TSelf left, TOther right) => left <= right; + } + + [RequiresPreviewFeatures] + public static class DecrementOperatorsHelper + where TSelf : IDecrementOperators +{ + public static TSelf op_Decrement(TSelf value) => --value; + } + + [RequiresPreviewFeatures] + public static class DivisionOperatorsHelper + where TSelf : IDivisionOperators + { + public static TResult op_Division(TSelf left, TOther right) => left / right; + } + + [RequiresPreviewFeatures] + public static class EqualityOperatorsHelper + where TSelf : IEqualityOperators + { + public static bool op_Equality(TSelf left, TOther right) => left == right; + + public static bool op_Inequality(TSelf left, TOther right) => left != right; + } + + [RequiresPreviewFeatures] + public static class IncrementOperatorsHelper + where TSelf : IIncrementOperators + { + public static TSelf op_Increment(TSelf value) => ++value; + } + + [RequiresPreviewFeatures] + public static class ModulusOperatorsHelper + where TSelf : IModulusOperators + { + public static TResult op_Modulus(TSelf left, TOther right) => left % right; + } + + [RequiresPreviewFeatures] + public static class MultiplyOperatorsHelper + where TSelf : IMultiplyOperators + { + public static TResult op_Multiply(TSelf left, TOther right) => left * right; + } + + [RequiresPreviewFeatures] + public static class MinMaxValueHelper + where TSelf : IMinMaxValue + { + public static TSelf MaxValue => TSelf.MaxValue; + + public static TSelf MinValue => TSelf.MinValue; + } + + [RequiresPreviewFeatures] + public static class MultiplicativeIdentityHelper + where TSelf : IMultiplicativeIdentity + { + public static TResult MultiplicativeIdentity => TSelf.MultiplicativeIdentity; + } + + [RequiresPreviewFeatures] + public static class NumberHelper + where TSelf : INumber + { + public static TSelf One => TSelf.One; + + public static TSelf Zero => TSelf.Zero; + + public static TSelf Abs(TSelf value) => TSelf.Abs(value); + + public static TSelf Clamp(TSelf value, TSelf min, TSelf max) => TSelf.Clamp(value, min, max); + + public static TSelf Create(TOther value) + where TOther : INumber => TSelf.Create(value); + + public static TSelf CreateSaturating(TOther value) + where TOther : INumber => TSelf.CreateSaturating(value); + + public static TSelf CreateTruncating(TOther value) + where TOther : INumber => TSelf.CreateTruncating(value); + + public static (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) => TSelf.DivRem(left, right); + + public static TSelf Max(TSelf x, TSelf y) => TSelf.Max(x, y); + + public static TSelf Min(TSelf x, TSelf y) => TSelf.Min(x, y); + + public static TSelf Parse(string s, NumberStyles style, IFormatProvider provider) => TSelf.Parse(s, style, provider); + + public static TSelf Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => TSelf.Parse(s, style, provider); + + public static TSelf Sign(TSelf value) => TSelf.Sign(value); + + public static bool TryCreate(TOther value, out TSelf result) + where TOther : INumber => TSelf.TryCreate(value, out result); + + public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, style, provider, out result); + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, style, provider, out result); + } + + [RequiresPreviewFeatures] + public static class ParseableHelper + where TSelf : IParseable + { + public static TSelf Parse(string s, IFormatProvider provider) => TSelf.Parse(s, provider); + + public static bool TryParse(string s, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, provider, out result); + } + + [RequiresPreviewFeatures] + public static class ShiftOperatorsHelper + where TSelf : IShiftOperators + { + public static TResult op_LeftShift(TSelf value, int shiftAmount) => value << shiftAmount; + + public static TResult op_RightShift(TSelf value, int shiftAmount) => value >> shiftAmount; + } + + [RequiresPreviewFeatures] + public static class SignedNumberHelper + where TSelf : ISignedNumber + { + public static TSelf NegativeOne => TSelf.NegativeOne; + } + + [RequiresPreviewFeatures] + public static class SpanParseableHelper + where TSelf : ISpanParseable + { + public static TSelf Parse(ReadOnlySpan s, IFormatProvider provider) => TSelf.Parse(s, provider); + + public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, provider, out result); + } + + [RequiresPreviewFeatures] + public static class SubtractionOperatorsHelper + where TSelf : ISubtractionOperators + { + public static TResult op_Subtraction(TSelf left, TOther right) => left - right; + } + + [RequiresPreviewFeatures] + public static class UnaryNegationOperatorsHelper + where TSelf : IUnaryNegationOperators + { + public static TResult op_UnaryNegation(TSelf value) => -value; + } + + [RequiresPreviewFeatures] + public static class UnaryPlusOperatorsHelper + where TSelf : IUnaryPlusOperators + { + public static TResult op_UnaryPlus(TSelf value) => +value; + } +} diff --git a/src/libraries/System.Runtime/tests/System/GenericMathTests.cs b/src/libraries/System.Runtime/tests/System/GenericMathTests.cs deleted file mode 100644 index ccd3b8a269fca..0000000000000 --- a/src/libraries/System.Runtime/tests/System/GenericMathTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Linq; -using Xunit; - -namespace System.Tests -{ - public static class GenericMath - { - public static TResult Average(IEnumerable values) - where TSelf : INumber - where TResult : INumber - { - TResult sum = Sum(values); - return TResult.Create(sum) / TResult.Create(values.Count()); - } - - public static TResult StandardDeviation(IEnumerable values) - where TSelf : INumber - where TResult : IFloatingPoint - { - TResult standardDeviation = TResult.Zero; - - if (values.Any()) - { - TResult average = Average(values); - TResult sum = Sum(values.Select((value) => { - var deviation = TResult.Create(value) - average; - return deviation * deviation; - })); - standardDeviation = TResult.Sqrt(sum / TResult.Create(values.Count() - 1)); - } - - return standardDeviation; - } - - public static TResult Sum(IEnumerable values) - where TSelf : INumber - where TResult : INumber - { - TResult result = TResult.Zero; - - foreach (var value in values) - { - result += TResult.Create(value); - } - - return result; - } - } - - public abstract class GenericMathTests - where TSelf : INumber - { - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void AverageTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void StandardDeviationTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void SumTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void SumInt32Test(); - } -} diff --git a/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs new file mode 100644 index 0000000000000..667a5f55ac22f --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int16Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((short)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((short)0x8000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((short)0x7FFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((short)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((short)0xFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((short)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((short)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((short)0x0001, AdditionOperatorsHelper.op_Addition((short)0x0000, (short)1)); + Assert.Equal((short)0x0002, AdditionOperatorsHelper.op_Addition((short)0x0001, (short)1)); + Assert.Equal(unchecked((short)0x8000), AdditionOperatorsHelper.op_Addition((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), AdditionOperatorsHelper.op_Addition(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0000, AdditionOperatorsHelper.op_Addition(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((short)0x0010, BinaryIntegerHelper.LeadingZeroCount((short)0x0000)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.LeadingZeroCount((short)0x0001)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.LeadingZeroCount((short)0x7FFF)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.LeadingZeroCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BinaryIntegerHelper.LeadingZeroCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.PopCount((short)0x0000)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.PopCount((short)0x0001)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.PopCount((short)0x7FFF)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.PopCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0010, BinaryIntegerHelper.PopCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.RotateLeft((short)0x0000, 1)); + Assert.Equal((short)0x0002, BinaryIntegerHelper.RotateLeft((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xFFFE), BinaryIntegerHelper.RotateLeft((short)0x7FFF, 1)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.RotateLeft(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), BinaryIntegerHelper.RotateLeft(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.RotateRight((short)0x0000, 1)); + Assert.Equal(unchecked((short)0x8000), BinaryIntegerHelper.RotateRight((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xBFFF), BinaryIntegerHelper.RotateRight((short)0x7FFF, 1)); + Assert.Equal((short)0x4000, BinaryIntegerHelper.RotateRight(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), BinaryIntegerHelper.RotateRight(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((short)0x0010, BinaryIntegerHelper.TrailingZeroCount((short)0x0000)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount((short)0x0001)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount((short)0x7FFF)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.TrailingZeroCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((short)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((short)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((short)0x7FFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((short)0x8000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((short)0xFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((short)0x0000, BinaryNumberHelper.Log2((short)0x0000)); + Assert.Equal((short)0x0000, BinaryNumberHelper.Log2((short)0x0001)); + Assert.Equal((short)0x000E, BinaryNumberHelper.Log2((short)0x7FFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((short)0x8000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x0001, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x7FFF, (short)1)); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x0000, (short)1)); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((short)0xFFFF), BitwiseOperatorsHelper.op_OnesComplement((short)0x0000)); + Assert.Equal(unchecked((short)0xFFFE), BitwiseOperatorsHelper.op_OnesComplement((short)0x0001)); + Assert.Equal(unchecked((short)0x8000), BitwiseOperatorsHelper.op_OnesComplement((short)0x7FFF)); + Assert.Equal((short)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((short)0x0000, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((short)0x0001, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((short)0x7FFF, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((short)0x8000), (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x0000, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x0001, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x7FFF, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((short)0x8000), (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((short)0x0000, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((short)0x0001, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((short)0x7FFF, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((short)0x8000), (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x0000, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x0001, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x7FFF, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((short)0x8000), (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((short)0xFFFF), DecrementOperatorsHelper.op_Decrement((short)0x0000)); + Assert.Equal((short)0x0000, DecrementOperatorsHelper.op_Decrement((short)0x0001)); + Assert.Equal((short)0x7FFE, DecrementOperatorsHelper.op_Decrement((short)0x7FFF)); + Assert.Equal((short)0x7FFF, DecrementOperatorsHelper.op_Decrement(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division((short)0x0000, (short)2)); + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division((short)0x0001, (short)2)); + Assert.Equal((short)0x3FFF, DivisionOperatorsHelper.op_Division((short)0x7FFF, (short)2)); + Assert.Equal(unchecked((short)0xC000), DivisionOperatorsHelper.op_Division(unchecked((short)0x8000), (short)2)); + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((short)0x0000, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((short)0x0001, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((short)0x7FFF, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((short)0x8000), (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((short)0x0000, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((short)0x0001, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((short)0x7FFF, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((short)0x8000), (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((short)0x0001, IncrementOperatorsHelper.op_Increment((short)0x0000)); + Assert.Equal((short)0x0002, IncrementOperatorsHelper.op_Increment((short)0x0001)); + Assert.Equal(unchecked((short)0x8000), IncrementOperatorsHelper.op_Increment((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8001), IncrementOperatorsHelper.op_Increment(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, IncrementOperatorsHelper.op_Increment(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((short)0x0000, ModulusOperatorsHelper.op_Modulus((short)0x0000, (short)2)); + Assert.Equal((short)0x0001, ModulusOperatorsHelper.op_Modulus((short)0x0001, (short)2)); + Assert.Equal((short)0x0001, ModulusOperatorsHelper.op_Modulus((short)0x7FFF, (short)2)); + Assert.Equal((short)0x0000, ModulusOperatorsHelper.op_Modulus(unchecked((short)0x8000), (short)2)); + Assert.Equal(unchecked((short)0xFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((short)0x0000, MultiplyOperatorsHelper.op_Multiply((short)0x0000, (short)2)); + Assert.Equal((short)0x0002, MultiplyOperatorsHelper.op_Multiply((short)0x0001, (short)2)); + Assert.Equal(unchecked((short)0xFFFE), MultiplyOperatorsHelper.op_Multiply((short)0x7FFF, (short)2)); + Assert.Equal((short)0x0000, MultiplyOperatorsHelper.op_Multiply(unchecked((short)0x8000), (short)2)); + Assert.Equal(unchecked((short)0xFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((short)0x0000, NumberHelper.Abs((short)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Abs((short)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Abs((short)0x7FFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((short)0x8000))); + Assert.Equal((short)0x0001, NumberHelper.Abs(unchecked((short)0xFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((short)0x0000, NumberHelper.Clamp((short)0x0000, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal((short)0x0001, NumberHelper.Clamp((short)0x0001, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal((short)0x003F, NumberHelper.Clamp((short)0x7FFF, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal(unchecked((short)0xFFC0), NumberHelper.Clamp(unchecked((short)0x8000), unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Clamp(unchecked((short)0xFFFF), unchecked((short)0xFFC0), (short)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((short)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((short)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((short)0x0000, (short)0x0000), NumberHelper.DivRem((short)0x0000, (short)2)); + Assert.Equal(((short)0x0000, (short)0x0001), NumberHelper.DivRem((short)0x0001, (short)2)); + Assert.Equal(((short)0x3FFF, (short)0x0001), NumberHelper.DivRem((short)0x7FFF, (short)2)); + Assert.Equal((unchecked((short)0xC000), (short)0x0000), NumberHelper.DivRem(unchecked((short)0x8000), (short)2)); + Assert.Equal(((short)0x0000, unchecked((short)0xFFFF)), NumberHelper.DivRem(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((short)0x0001, NumberHelper.Max((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFF, NumberHelper.Max((short)0x7FFF, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((short)0x0000, NumberHelper.Min((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Min((short)0x0001, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Min((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.Min(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Min(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((short)0x0000, NumberHelper.Sign((short)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Sign((short)0x0001)); + Assert.Equal((short)0x0001, NumberHelper.Sign((short)0x7FFF)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Sign(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Sign(unchecked((short)0xFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((short)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((short)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((short)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + short result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((short)0x8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + short result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((short)0x007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((short)0xFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + short result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_LeftShift((short)0x0000, 1)); + Assert.Equal((short)0x0002, ShiftOperatorsHelper.op_LeftShift((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xFFFE), ShiftOperatorsHelper.op_LeftShift((short)0x7FFF, 1)); + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_LeftShift(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_RightShift((short)0x0000, 1)); + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_RightShift((short)0x0001, 1)); + Assert.Equal((short)0x3FFF, ShiftOperatorsHelper.op_RightShift((short)0x7FFF, 1)); + Assert.Equal(unchecked((short)0xC000), ShiftOperatorsHelper.op_RightShift(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((short)0xFFFF), SubtractionOperatorsHelper.op_Subtraction((short)0x0000, (short)1)); + Assert.Equal((short)0x0000, SubtractionOperatorsHelper.op_Subtraction((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((short)0x7FFF, (short)1)); + Assert.Equal((short)0x7FFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((short)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x0000)); + Assert.Equal(unchecked((short)0xFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x0001)); + Assert.Equal(unchecked((short)0x8001), UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((short)0x8000))); + Assert.Equal((short)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((short)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x0000)); + Assert.Equal((short)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x0001)); + Assert.Equal((short)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((short)0xFFFF))); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Valid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, short expected) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Invalid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int16Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, short expected) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Invalid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(short), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(short), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs b/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs deleted file mode 100644 index c8f469d6eb3a8..0000000000000 --- a/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Linq; -using Xunit; - -namespace System.Tests -{ - public sealed class Int32GenericMathTests : GenericMathTests - { - public override void AverageTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 16383.5, actual: GenericMath.Average(values)); - } - - public override void StandardDeviationTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 9459.451146868934, actual: GenericMath.StandardDeviation(values)); - } - - public override void SumTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 536854528, actual: GenericMath.Sum(values)); - } - - public override void SumInt32Test() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 536854528, actual: GenericMath.Sum(values)); - } - } -} diff --git a/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs new file mode 100644 index 0000000000000..28f3c06c40393 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int32Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((int)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((int)0x80000000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((int)0x7FFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((int)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((int)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((int)0x00000001, AdditionOperatorsHelper.op_Addition((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, AdditionOperatorsHelper.op_Addition((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0x80000000), AdditionOperatorsHelper.op_Addition((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), AdditionOperatorsHelper.op_Addition(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000000, AdditionOperatorsHelper.op_Addition(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((int)0x00000020, BinaryIntegerHelper.LeadingZeroCount((int)0x00000000)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.LeadingZeroCount((int)0x00000001)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.LeadingZeroCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.PopCount((int)0x00000000)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.PopCount((int)0x00000001)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.PopCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.PopCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000020, BinaryIntegerHelper.PopCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.RotateLeft((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, BinaryIntegerHelper.RotateLeft((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BinaryIntegerHelper.RotateLeft((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.RotateLeft(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.RotateRight((int)0x00000000, 1)); + Assert.Equal(unchecked((int)0x80000000), BinaryIntegerHelper.RotateRight((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xBFFFFFFF), BinaryIntegerHelper.RotateRight((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x40000000, BinaryIntegerHelper.RotateRight(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((int)0x00000020, BinaryIntegerHelper.TrailingZeroCount((int)0x00000000)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount((int)0x00000001)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.TrailingZeroCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((int)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((int)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((int)0x7FFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((int)0x80000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((int)0x00000000, BinaryNumberHelper.Log2((int)0x00000000)); + Assert.Equal((int)0x00000000, BinaryNumberHelper.Log2((int)0x00000001)); + Assert.Equal((int)0x0000001E, BinaryNumberHelper.Log2((int)0x7FFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((int)0x80000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x00000001, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((int)0x00000000)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000000), BitwiseOperatorsHelper.op_OnesComplement((int)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((int)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((int)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((int)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((int)0x80000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((int)0x80000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((int)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((int)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((int)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((int)0x80000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((int)0x80000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), DecrementOperatorsHelper.op_Decrement((int)0x00000000)); + Assert.Equal((int)0x00000000, DecrementOperatorsHelper.op_Decrement((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((int)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division((int)0x00000000, 2)); + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division((int)0x00000001, 2)); + Assert.Equal((int)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((int)0x7FFFFFFF, 2)); + Assert.Equal(unchecked((int)0xC0000000), DivisionOperatorsHelper.op_Division(unchecked((int)0x80000000), 2)); + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((int)0x00000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((int)0x00000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((int)0x7FFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((int)0x80000000), 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((int)0x00000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((int)0x00000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((int)0x7FFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((int)0x80000000), 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((int)0x00000001, IncrementOperatorsHelper.op_Increment((int)0x00000000)); + Assert.Equal((int)0x00000002, IncrementOperatorsHelper.op_Increment((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000000), IncrementOperatorsHelper.op_Increment((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000001), IncrementOperatorsHelper.op_Increment(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, IncrementOperatorsHelper.op_Increment(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((int)0x00000000, ModulusOperatorsHelper.op_Modulus((int)0x00000000, 2)); + Assert.Equal((int)0x00000001, ModulusOperatorsHelper.op_Modulus((int)0x00000001, 2)); + Assert.Equal((int)0x00000001, ModulusOperatorsHelper.op_Modulus((int)0x7FFFFFFF, 2)); + Assert.Equal((int)0x00000000, ModulusOperatorsHelper.op_Modulus(unchecked((int)0x80000000), 2)); + Assert.Equal(unchecked((int)0xFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((int)0x00000000, MultiplyOperatorsHelper.op_Multiply((int)0x00000000, 2)); + Assert.Equal((int)0x00000002, MultiplyOperatorsHelper.op_Multiply((int)0x00000001, 2)); + Assert.Equal(unchecked((int)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((int)0x7FFFFFFF, 2)); + Assert.Equal((int)0x00000000, MultiplyOperatorsHelper.op_Multiply(unchecked((int)0x80000000), 2)); + Assert.Equal(unchecked((int)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Abs((int)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Abs((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Abs((int)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000001, NumberHelper.Abs(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Clamp((int)0x00000000, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal((int)0x00000001, NumberHelper.Clamp((int)0x00000001, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal((int)0x0000003F, NumberHelper.Clamp((int)0x7FFFFFFF, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((int)0xFFFFFFC0), NumberHelper.Clamp(unchecked((int)0x80000000), unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Clamp(unchecked((int)0xFFFFFFFF), unchecked((int)0xFFFFFFC0), 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Create(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((int)0x7FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((int)0x7FFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((int)0x00000000, (int)0x00000000), NumberHelper.DivRem((int)0x00000000, 2)); + Assert.Equal(((int)0x00000000, (int)0x00000001), NumberHelper.DivRem((int)0x00000001, 2)); + Assert.Equal(((int)0x3FFFFFFF, (int)0x00000001), NumberHelper.DivRem((int)0x7FFFFFFF, 2)); + Assert.Equal((unchecked((int)0xC0000000), (int)0x00000000), NumberHelper.DivRem(unchecked((int)0x80000000), 2)); + Assert.Equal(((int)0x00000000, unchecked((int)0xFFFFFFFF)), NumberHelper.DivRem(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((int)0x00000001, NumberHelper.Max((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Max((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Min((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Min((int)0x00000001, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Min((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Min(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Min(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Sign((int)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Sign((int)0x00000001)); + Assert.Equal((int)0x00000001, NumberHelper.Sign((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Sign(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Sign(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((int)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((int)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((int)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + int result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((int)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((int)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((int)0xFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((int)0x80000000)), out result)); + Assert.Equal(unchecked((int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((int)0xFFFFFFFF)), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + int result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(unchecked((int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((int)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((int)0xFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((int)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((int)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(unchecked((int)0x00000000), result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(unchecked((int)0x00000000), result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + int result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal(unchecked((int)0x00000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0x00000000), result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_LeftShift((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, ShiftOperatorsHelper.op_LeftShift((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_LeftShift(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_RightShift((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_RightShift((int)0x00000001, 1)); + Assert.Equal((int)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0xC0000000), ShiftOperatorsHelper.op_RightShift(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, SubtractionOperatorsHelper.op_Subtraction((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((int)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x00000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000001), UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((int)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x00000000)); + Assert.Equal((int)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((int)0xFFFFFFFF))); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Valid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, int expected) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Invalid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int32Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, int expected) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Invalid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(int), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(int), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs new file mode 100644 index 0000000000000..ddc380cc36e22 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int64Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((long)0x0000000000000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((long)0x8000000000000000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((long)0x0000000000000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((long)0x0000000000000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((long)0x0000000000000001, AdditionOperatorsHelper.op_Addition((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, AdditionOperatorsHelper.op_Addition((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), AdditionOperatorsHelper.op_Addition((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000000, AdditionOperatorsHelper.op_Addition(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.LeadingZeroCount((long)0x0000000000000000)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.LeadingZeroCount((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.PopCount((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.PopCount((long)0x0000000000000001)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.PopCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.PopCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.PopCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.RotateLeft((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, BinaryIntegerHelper.RotateLeft((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.RotateLeft(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.RotateRight((long)0x0000000000000000, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), BinaryIntegerHelper.RotateRight((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x4000000000000000, BinaryIntegerHelper.RotateRight(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.TrailingZeroCount((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.TrailingZeroCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((long)0x0000000000000000)); + Assert.True(BinaryNumberHelper.IsPow2((long)0x0000000000000001)); + Assert.False(BinaryNumberHelper.IsPow2((long)0x7FFFFFFFFFFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((long)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((long)0x0000000000000000, BinaryNumberHelper.Log2((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, BinaryNumberHelper.Log2((long)0x0000000000000001)); + Assert.Equal((long)0x000000000000003E, BinaryNumberHelper.Log2((long)0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((long)0x8000000000000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x0000000000000001, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((long)0x0000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((long)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((long)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((long)0x8000000000000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((long)0x8000000000000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((long)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((long)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((long)0x8000000000000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((long)0x8000000000000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, DecrementOperatorsHelper.op_Decrement((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division((long)0x0000000000000001, 2)); + Assert.Equal((long)0x3FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal(unchecked((long)0xC000000000000000), DivisionOperatorsHelper.op_Division(unchecked((long)0x8000000000000000), 2)); + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((long)0x0000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((long)0x0000000000000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((long)0x8000000000000000), 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((long)0x0000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((long)0x0000000000000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((long)0x8000000000000000), 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((long)0x0000000000000001, IncrementOperatorsHelper.op_Increment((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000002, IncrementOperatorsHelper.op_Increment((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000000), IncrementOperatorsHelper.op_Increment((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, IncrementOperatorsHelper.op_Increment(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((long)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((long)0x0000000000000001, 2)); + Assert.Equal((long)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((long)0x0000000000000000, ModulusOperatorsHelper.op_Modulus(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((long)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000002, MultiplyOperatorsHelper.op_Multiply((long)0x0000000000000001, 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((long)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Abs((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Abs((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Abs((long)0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Abs(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Clamp((long)0x0000000000000000, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Clamp((long)0x0000000000000001, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal((long)0x000000000000003F, NumberHelper.Clamp((long)0x7FFFFFFFFFFFFFFF, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFC0), NumberHelper.Clamp(unchecked((long)0x8000000000000000), unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Clamp(unchecked((long)0xFFFFFFFFFFFFFFFF), unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.Create(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Create(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.Create(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.Create(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateSaturating(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((long)0x0000000000000000, (long)0x0000000000000000), NumberHelper.DivRem((long)0x0000000000000000, 2)); + Assert.Equal(((long)0x0000000000000000, (long)0x0000000000000001), NumberHelper.DivRem((long)0x0000000000000001, 2)); + Assert.Equal(((long)0x3FFFFFFFFFFFFFFF, (long)0x0000000000000001), NumberHelper.DivRem((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((unchecked((long)0xC000000000000000), (long)0x0000000000000000), NumberHelper.DivRem(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(((long)0x0000000000000000, unchecked((long)0xFFFFFFFFFFFFFFFF)), NumberHelper.DivRem(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((long)0x0000000000000001, NumberHelper.Max((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Max((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Min((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Min((long)0x0000000000000001, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Min((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Min(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Min(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Sign((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Sign((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Sign((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((long)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((long)0x0000000000000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((long)0x00000000000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + long result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((long)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((long)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((long)0x8000000000000000)), out result)); + Assert.Equal(unchecked((long)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + long result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((long)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((long)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((long)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((long)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((long)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((long)0x00000000FFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + long result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((long)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((long)0x00000000FFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, ShiftOperatorsHelper.op_LeftShift((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((long)0x0000000000000001, 1)); + Assert.Equal((long)0x3FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0xC000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, SubtractionOperatorsHelper.op_Subtraction((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((long)0x0000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x0000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((long)0x0000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Valid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, long expected) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Invalid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int64Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, long expected) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Invalid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(long), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(long), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs new file mode 100644 index 0000000000000..1f5b4144f94ca --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs @@ -0,0 +1,1746 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class IntPtrTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((nint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x8000000000000000), MinMaxValueHelper.MinValue); + } + else + { + Assert.Equal(unchecked((nint)0x80000000), MinMaxValueHelper.MinValue); + } + } + + [Fact] + public static void MaxValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), MinMaxValueHelper.MaxValue); + } + else + { + Assert.Equal((nint)0x7FFFFFFF, MinMaxValueHelper.MaxValue); + } + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((nint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), SignedNumberHelper.NegativeOne); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), SignedNumberHelper.NegativeOne); + } + } + + [Fact] + public static void OneTest() + { + Assert.Equal((nint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000002), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, AdditionOperatorsHelper.op_Addition((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000002, AdditionOperatorsHelper.op_Addition((nint)0x00000001, (nint)1)); + Assert.Equal(unchecked((nint)0x80000000), AdditionOperatorsHelper.op_Addition((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000000, AdditionOperatorsHelper.op_Addition(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void LeadingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x0000000000000020, BinaryIntegerHelper.LeadingZeroCount((nint)0x00000000)); + Assert.Equal((nint)0x000000000000001F, BinaryIntegerHelper.LeadingZeroCount((nint)0x00000001)); + Assert.Equal((nint)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void PopCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.PopCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.PopCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.PopCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.PopCount((nint)0x00000000)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.PopCount((nint)0x00000001)); + Assert.Equal((nint)0x0000001F, BinaryIntegerHelper.PopCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.PopCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000020, BinaryIntegerHelper.PopCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void RotateLeftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000002), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.RotateLeft((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000002, BinaryIntegerHelper.RotateLeft((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BinaryIntegerHelper.RotateLeft((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.RotateLeft(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void RotateRightTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x8000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x4000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.RotateRight((nint)0x00000000, 1)); + Assert.Equal(unchecked((nint)0x80000000), BinaryIntegerHelper.RotateRight((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xBFFFFFFF), BinaryIntegerHelper.RotateRight((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x40000000, BinaryIntegerHelper.RotateRight(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void TrailingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((nint)0x00000000)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nint)0x00000001)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void IsPow2Test() + { + if (Environment.Is64BitProcess) + { + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x0000000000000000))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nint)0x0000000000000001))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.False(BinaryNumberHelper.IsPow2((nint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((nint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((nint)0x7FFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x80000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void Log2Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x000000000000003E), BinaryNumberHelper.Log2(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, BinaryNumberHelper.Log2((nint)0x00000000)); + Assert.Equal((nint)0x00000000, BinaryNumberHelper.Log2((nint)0x00000001)); + Assert.Equal((nint)0x0000001E, BinaryNumberHelper.Log2((nint)0x7FFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0x80000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_BitwiseAndTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_BitwiseOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_ExclusiveOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_OnesComplementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((nint)0x00000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000000), BitwiseOperatorsHelper.op_OnesComplement((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_LessThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((nint)0x00000000, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nint)0x00000001, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nint)0x7FFFFFFF, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x80000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x00000000, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x00000001, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x7FFFFFFF, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x80000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_GreaterThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nint)0x00000000, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nint)0x00000001, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nint)0x7FFFFFFF, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x80000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x00000000, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x00000001, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x7FFFFFFF, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x80000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_DecrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), DecrementOperatorsHelper.op_Decrement((nint)0x00000000)); + Assert.Equal((nint)0x00000000, DecrementOperatorsHelper.op_Decrement((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_DivisionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0x3FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0xC000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division((nint)0x00000001, (nint)2)); + Assert.Equal((nint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal(unchecked((nint)0xC0000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void op_EqualityTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(EqualityOperatorsHelper.op_Equality((nint)0x00000000, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((nint)0x00000001, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nint)0x7FFFFFFF, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x80000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_InequalityTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(EqualityOperatorsHelper.op_Inequality((nint)0x00000000, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((nint)0x00000001, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nint)0x7FFFFFFF, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x80000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_IncrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000002), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000001, IncrementOperatorsHelper.op_Increment((nint)0x00000000)); + Assert.Equal((nint)0x00000002, IncrementOperatorsHelper.op_Increment((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000000), IncrementOperatorsHelper.op_Increment((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, IncrementOperatorsHelper.op_Increment(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_ModulusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, ModulusOperatorsHelper.op_Modulus((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000001, ModulusOperatorsHelper.op_Modulus((nint)0x00000001, (nint)2)); + Assert.Equal((nint)0x00000001, ModulusOperatorsHelper.op_Modulus((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((nint)0x00000000, ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void op_MultiplyTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000002), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000002, MultiplyOperatorsHelper.op_Multiply((nint)0x00000001, (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((nint)0x00000000, MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void AbsTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Abs(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Abs(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Abs(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Abs(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Abs((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Abs((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Abs((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000001, NumberHelper.Abs(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void ClampTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Clamp(unchecked((nint)0x0000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Clamp(unchecked((nint)0x0000000000000001), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0x000000000000003F), NumberHelper.Clamp(unchecked((nint)0x7FFFFFFFFFFFFFFF), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFC0), NumberHelper.Clamp(unchecked((nint)0x8000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Clamp(unchecked((nint)0xFFFFFFFFFFFFFFFF), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Clamp((nint)0x00000000, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal((nint)0x00000001, NumberHelper.Clamp((nint)0x00000001, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal((nint)0x0000003F, NumberHelper.Clamp((nint)0x7FFFFFFF, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal(unchecked((nint)0xFFFFFFC0), NumberHelper.Clamp(unchecked((nint)0x80000000), unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Clamp(unchecked((nint)0xFFFFFFFF), unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + } + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.Create(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Create(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x00000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x00000001)); + Assert.Equal(unchecked((nint)0x000000007FFFFFFF), NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x0000000080000000), NumberHelper.Create(0x80000000)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), NumberHelper.Create(0xFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateSaturating(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((nint)0x000000007FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x0000000080000000), NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x80000000))); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((nint)0x0000000000007FFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nint)0xFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x00)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x01)); + Assert.Equal(unchecked((nint)0x000000000000007F), NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nint)0xFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(unchecked((nuint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void DivRemTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal((unchecked((nint)0x3FFFFFFFFFFFFFFF), unchecked((nint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal((unchecked((nint)0xC000000000000000), unchecked((nint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFFF)), NumberHelper.DivRem(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal(((nint)0x00000000, (nint)0x00000000), NumberHelper.DivRem((nint)0x00000000, (nint)2)); + Assert.Equal(((nint)0x00000000, (nint)0x00000001), NumberHelper.DivRem((nint)0x00000001, (nint)2)); + Assert.Equal(((nint)0x3FFFFFFF, (nint)0x00000001), NumberHelper.DivRem((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((unchecked((nint)0xC0000000), (nint)0x00000000), NumberHelper.DivRem(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(((nint)0x00000000, unchecked((nint)0xFFFFFFFF)), NumberHelper.DivRem(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void MaxTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, NumberHelper.Max((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Max((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void MinTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Min(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Min(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Min(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Min(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Min(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Min((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Min((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Min((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.Min(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Min(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void SignTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Sign(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Sign(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Sign(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Sign((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Sign((nint)0x00000001)); + Assert.Equal((nint)0x00000001, NumberHelper.Sign((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Sign(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Sign(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((nint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((nint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((nint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((nint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(unchecked((nint)(int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(unchecked((nint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((nint)0x80000000)), out result)); + Assert.Equal(unchecked((nint)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((nint)0xFFFFFFFF)), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((nint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((nint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(unchecked((nint)0x0000000080000000), result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nint)0x00000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nint)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((nint)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((nint)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked(unchecked((nuint)0x80000000)), out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked(unchecked((nuint)0xFFFFFFFF)), out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000002), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000002, ShiftOperatorsHelper.op_LeftShift((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void op_RightShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0x3FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0xC000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_RightShift((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_RightShift((nint)0x00000001, 1)); + Assert.Equal((nint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((nint)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((nint)0xC0000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void op_SubtractionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_UnaryNegationTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x00000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000001), UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_UnaryPlusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x00000000)); + Assert.Equal((nint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0xFFFFFFFF))); + } + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Valid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, nint expected) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Invalid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, nint expected) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Invalid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(nint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(nint), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs new file mode 100644 index 0000000000000..82877a1e2d609 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class SByteTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((sbyte)0x00, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((sbyte)0x80), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((sbyte)0x7F, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((sbyte)0x01, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((sbyte)0xFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((sbyte)0x01, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((sbyte)0x01, AdditionOperatorsHelper.op_Addition((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x02, AdditionOperatorsHelper.op_Addition((sbyte)0x01, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x80), AdditionOperatorsHelper.op_Addition((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), AdditionOperatorsHelper.op_Addition(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x00, AdditionOperatorsHelper.op_Addition(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x00)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x01)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.LeadingZeroCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.LeadingZeroCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.PopCount((sbyte)0x00)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.PopCount((sbyte)0x01)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.PopCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.PopCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.PopCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.RotateLeft((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x02, BinaryIntegerHelper.RotateLeft((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xFE), BinaryIntegerHelper.RotateLeft((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.RotateLeft(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), BinaryIntegerHelper.RotateLeft(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.RotateRight((sbyte)0x00, 1)); + Assert.Equal(unchecked((sbyte)0x80), BinaryIntegerHelper.RotateRight((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xBF), BinaryIntegerHelper.RotateRight((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x40, BinaryIntegerHelper.RotateRight(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), BinaryIntegerHelper.RotateRight(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x00)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x01)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.TrailingZeroCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((sbyte)0x00)); + Assert.True(BinaryNumberHelper.IsPow2((sbyte)0x01)); + Assert.False(BinaryNumberHelper.IsPow2((sbyte)0x7F)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((sbyte)0x80))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((sbyte)0x00, BinaryNumberHelper.Log2((sbyte)0x00)); + Assert.Equal((sbyte)0x00, BinaryNumberHelper.Log2((sbyte)0x01)); + Assert.Equal((sbyte)0x06, BinaryNumberHelper.Log2((sbyte)0x7F)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((sbyte)0x80))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7F, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7E, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((sbyte)0xFF), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x00)); + Assert.Equal(unchecked((sbyte)0xFE), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x80), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x7F)); + Assert.Equal((sbyte)0x7F, BitwiseOperatorsHelper.op_OnesComplement(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_OnesComplement(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((sbyte)0x00, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((sbyte)0x01, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((sbyte)0x7F, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x00, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x01, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x7F, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x00, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x01, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x7F, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x00, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x01, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x7F, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((sbyte)0xFF), DecrementOperatorsHelper.op_Decrement((sbyte)0x00)); + Assert.Equal((sbyte)0x00, DecrementOperatorsHelper.op_Decrement((sbyte)0x01)); + Assert.Equal((sbyte)0x7E, DecrementOperatorsHelper.op_Decrement((sbyte)0x7F)); + Assert.Equal((sbyte)0x7F, DecrementOperatorsHelper.op_Decrement(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFE), DecrementOperatorsHelper.op_Decrement(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division((sbyte)0x01, (sbyte)2)); + Assert.Equal((sbyte)0x3F, DivisionOperatorsHelper.op_Division((sbyte)0x7F, (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xC0), DivisionOperatorsHelper.op_Division(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((sbyte)0x00, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((sbyte)0x01, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((sbyte)0x7F, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((sbyte)0x00, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((sbyte)0x01, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((sbyte)0x7F, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((sbyte)0x01, IncrementOperatorsHelper.op_Increment((sbyte)0x00)); + Assert.Equal((sbyte)0x02, IncrementOperatorsHelper.op_Increment((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x80), IncrementOperatorsHelper.op_Increment((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x81), IncrementOperatorsHelper.op_Increment(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, IncrementOperatorsHelper.op_Increment(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((sbyte)0x00, ModulusOperatorsHelper.op_Modulus((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x01, ModulusOperatorsHelper.op_Modulus((sbyte)0x01, (sbyte)2)); + Assert.Equal((sbyte)0x01, ModulusOperatorsHelper.op_Modulus((sbyte)0x7F, (sbyte)2)); + Assert.Equal((sbyte)0x00, ModulusOperatorsHelper.op_Modulus(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFF), ModulusOperatorsHelper.op_Modulus(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((sbyte)0x00, MultiplyOperatorsHelper.op_Multiply((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x02, MultiplyOperatorsHelper.op_Multiply((sbyte)0x01, (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFE), MultiplyOperatorsHelper.op_Multiply((sbyte)0x7F, (sbyte)2)); + Assert.Equal((sbyte)0x00, MultiplyOperatorsHelper.op_Multiply(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFE), MultiplyOperatorsHelper.op_Multiply(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Abs((sbyte)0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Abs((sbyte)0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Abs((sbyte)0x7F)); + Assert.Throws(() => NumberHelper.Abs(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x01, NumberHelper.Abs(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Clamp((sbyte)0x00, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal((sbyte)0x01, NumberHelper.Clamp((sbyte)0x01, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal((sbyte)0x3F, NumberHelper.Clamp((sbyte)0x7F, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal(unchecked((sbyte)0xC0), NumberHelper.Clamp(unchecked((sbyte)0x80), unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Clamp(unchecked((sbyte)0xFF), unchecked((sbyte)0xC0), (sbyte)0x3F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(0x80)); + Assert.Throws(() => NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((char)0x0001)); + Assert.Throws(() => NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x8000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateTruncating(0x80)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((sbyte)0x00, (sbyte)0x00), NumberHelper.DivRem((sbyte)0x00, (sbyte)2)); + Assert.Equal(((sbyte)0x00, (sbyte)0x01), NumberHelper.DivRem((sbyte)0x01, (sbyte)2)); + Assert.Equal(((sbyte)0x3F, (sbyte)0x01), NumberHelper.DivRem((sbyte)0x7F, (sbyte)2)); + Assert.Equal((unchecked((sbyte)0xC0), (sbyte)0x00), NumberHelper.DivRem(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(((sbyte)0x00, unchecked((sbyte)0xFF)), NumberHelper.DivRem(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((sbyte)0x01, NumberHelper.Max((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7F, NumberHelper.Max((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Min((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Min((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Min((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.Min(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Min(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Sign((sbyte)0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Sign((sbyte)0x01)); + Assert.Equal((sbyte)0x01, NumberHelper.Sign((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Sign(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Sign(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((sbyte)0x7F, result); + + Assert.False(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + sbyte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((sbyte)0x7F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((sbyte)0x80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + sbyte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_LeftShift((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x02, ShiftOperatorsHelper.op_LeftShift((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xFE), ShiftOperatorsHelper.op_LeftShift((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_LeftShift(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFE), ShiftOperatorsHelper.op_LeftShift(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_RightShift((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_RightShift((sbyte)0x01, 1)); + Assert.Equal((sbyte)0x3F, ShiftOperatorsHelper.op_RightShift((sbyte)0x7F, 1)); + Assert.Equal(unchecked((sbyte)0xC0), ShiftOperatorsHelper.op_RightShift(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), ShiftOperatorsHelper.op_RightShift(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((sbyte)0xFF), SubtractionOperatorsHelper.op_Subtraction((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x00, SubtractionOperatorsHelper.op_Subtraction((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7E, SubtractionOperatorsHelper.op_Subtraction((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x7F, SubtractionOperatorsHelper.op_Subtraction(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((sbyte)0x00, UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x00)); + Assert.Equal(unchecked((sbyte)0xFF), UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x81), UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x80), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x01, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((sbyte)0x00, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x00)); + Assert.Equal((sbyte)0x01, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x01)); + Assert.Equal((sbyte)0x7F, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x80), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((sbyte)0xFF))); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Valid_TestData), MemberType = typeof(SByteTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, sbyte expected) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Invalid_TestData), MemberType = typeof(SByteTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(SByteTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, sbyte expected) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Invalid_TestData), MemberType = typeof(SByteTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(sbyte), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(sbyte), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs new file mode 100644 index 0000000000000..982740495bb9b --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt16Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((ushort)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((ushort)0x0000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((ushort)0xFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((ushort)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((ushort)0x0001, AdditionOperatorsHelper.op_Addition((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0002, AdditionOperatorsHelper.op_Addition((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x8000, AdditionOperatorsHelper.op_Addition((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, AdditionOperatorsHelper.op_Addition((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0000, AdditionOperatorsHelper.op_Addition((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.LeadingZeroCount((ushort)0x0000)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.LeadingZeroCount((ushort)0x0001)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.LeadingZeroCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.LeadingZeroCount((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.LeadingZeroCount((ushort)0xFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.PopCount((ushort)0x0000)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.PopCount((ushort)0x0001)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.PopCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.PopCount((ushort)0x8000)); + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.PopCount((ushort)0xFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.RotateLeft((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0002, BinaryIntegerHelper.RotateLeft((ushort)0x0001, 1)); + Assert.Equal((ushort)0xFFFE, BinaryIntegerHelper.RotateLeft((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.RotateLeft((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFF, BinaryIntegerHelper.RotateLeft((ushort)0xFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.RotateRight((ushort)0x0000, 1)); + Assert.Equal((ushort)0x8000, BinaryIntegerHelper.RotateRight((ushort)0x0001, 1)); + Assert.Equal((ushort)0xBFFF, BinaryIntegerHelper.RotateRight((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x4000, BinaryIntegerHelper.RotateRight((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFF, BinaryIntegerHelper.RotateRight((ushort)0xFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.TrailingZeroCount((ushort)0x0000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0x0001)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.TrailingZeroCount((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0xFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((ushort)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((ushort)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((ushort)0x7FFF)); + Assert.True(BinaryNumberHelper.IsPow2((ushort)0x8000)); + Assert.False(BinaryNumberHelper.IsPow2((ushort)0xFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((ushort)0x0000, BinaryNumberHelper.Log2((ushort)0x0000)); + Assert.Equal((ushort)0x0000, BinaryNumberHelper.Log2((ushort)0x0001)); + Assert.Equal((ushort)0x000E, BinaryNumberHelper.Log2((ushort)0x7FFF)); + Assert.Equal((ushort)0x000F, BinaryNumberHelper.Log2((ushort)0x8000)); + Assert.Equal((ushort)0x000F, BinaryNumberHelper.Log2((ushort)0xFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((ushort)0xFFFF, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x0000)); + Assert.Equal((ushort)0xFFFE, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x0001)); + Assert.Equal((ushort)0x8000, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x7FFF)); + Assert.Equal((ushort)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_OnesComplement((ushort)0xFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((ushort)0x0000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x0001, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x7FFF, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x8000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x0000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x0001, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x7FFF, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x8000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x0000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x0001, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x7FFF, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x8000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x0000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x0001, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x7FFF, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x8000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((ushort)0xFFFF, DecrementOperatorsHelper.op_Decrement((ushort)0x0000)); + Assert.Equal((ushort)0x0000, DecrementOperatorsHelper.op_Decrement((ushort)0x0001)); + Assert.Equal((ushort)0x7FFE, DecrementOperatorsHelper.op_Decrement((ushort)0x7FFF)); + Assert.Equal((ushort)0x7FFF, DecrementOperatorsHelper.op_Decrement((ushort)0x8000)); + Assert.Equal((ushort)0xFFFE, DecrementOperatorsHelper.op_Decrement((ushort)0xFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((ushort)0x0000, DivisionOperatorsHelper.op_Division((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0000, DivisionOperatorsHelper.op_Division((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0x3FFF, DivisionOperatorsHelper.op_Division((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x4000, DivisionOperatorsHelper.op_Division((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0x7FFF, DivisionOperatorsHelper.op_Division((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x0000, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((ushort)0x0001, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x7FFF, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x8000, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x0000, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((ushort)0x0001, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x7FFF, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x8000, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((ushort)0x0001, IncrementOperatorsHelper.op_Increment((ushort)0x0000)); + Assert.Equal((ushort)0x0002, IncrementOperatorsHelper.op_Increment((ushort)0x0001)); + Assert.Equal((ushort)0x8000, IncrementOperatorsHelper.op_Increment((ushort)0x7FFF)); + Assert.Equal((ushort)0x8001, IncrementOperatorsHelper.op_Increment((ushort)0x8000)); + Assert.Equal((ushort)0x0000, IncrementOperatorsHelper.op_Increment((ushort)0xFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((ushort)0x0000, ModulusOperatorsHelper.op_Modulus((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x0000, ModulusOperatorsHelper.op_Modulus((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((ushort)0x0000, MultiplyOperatorsHelper.op_Multiply((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0002, MultiplyOperatorsHelper.op_Multiply((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0xFFFE, MultiplyOperatorsHelper.op_Multiply((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x0000, MultiplyOperatorsHelper.op_Multiply((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0xFFFE, MultiplyOperatorsHelper.op_Multiply((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Abs((ushort)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Abs((ushort)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Abs((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Abs((ushort)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Abs((ushort)0xFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.Clamp((ushort)0x0000, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x0001, NumberHelper.Clamp((ushort)0x0001, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0x7FFF, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0x8000, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0xFFFF, (ushort)0x0001, (ushort)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Create((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Create(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ushort)0xFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((ushort)0x0000, (ushort)0x0000), NumberHelper.DivRem((ushort)0x0000, (ushort)2)); + Assert.Equal(((ushort)0x0000, (ushort)0x0001), NumberHelper.DivRem((ushort)0x0001, (ushort)2)); + Assert.Equal(((ushort)0x3FFF, (ushort)0x0001), NumberHelper.DivRem((ushort)0x7FFF, (ushort)2)); + Assert.Equal(((ushort)0x4000, (ushort)0x0000), NumberHelper.DivRem((ushort)0x8000, (ushort)2)); + Assert.Equal(((ushort)0x7FFF, (ushort)0x0001), NumberHelper.DivRem((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.Max((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Max((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Max((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8000, NumberHelper.Max((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Max((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Min((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Sign((ushort)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x0001)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x7FFF)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x8000)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0xFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ushort)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((ushort)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((ushort)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((ushort)0x8000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((ushort)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + ushort result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ushort)0x007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((ushort)0x8000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((ushort)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + ushort result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_LeftShift((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0002, ShiftOperatorsHelper.op_LeftShift((ushort)0x0001, 1)); + Assert.Equal((ushort)0xFFFE, ShiftOperatorsHelper.op_LeftShift((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_LeftShift((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFE, ShiftOperatorsHelper.op_LeftShift((ushort)0xFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_RightShift((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_RightShift((ushort)0x0001, 1)); + Assert.Equal((ushort)0x3FFF, ShiftOperatorsHelper.op_RightShift((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x4000, ShiftOperatorsHelper.op_RightShift((ushort)0x8000, 1)); + Assert.Equal((ushort)0x7FFF, ShiftOperatorsHelper.op_RightShift((ushort)0xFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((ushort)0xFFFF, SubtractionOperatorsHelper.op_Subtraction((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0000, SubtractionOperatorsHelper.op_Subtraction((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x7FFF, SubtractionOperatorsHelper.op_Subtraction((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFE, SubtractionOperatorsHelper.op_Subtraction((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((ushort)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x0000)); + Assert.Equal((ushort)0xFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x0001)); + Assert.Equal((ushort)0x8001, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x8000)); + Assert.Equal((ushort)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0xFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((ushort)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x0000)); + Assert.Equal((ushort)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x0001)); + Assert.Equal((ushort)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x8000)); + Assert.Equal((ushort)0xFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0xFFFF)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Valid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, ushort expected) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Invalid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, ushort expected) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Invalid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(ushort), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(ushort), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs new file mode 100644 index 0000000000000..ab6a6e02471ed --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt32Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((uint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((uint)0x00000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((uint)0xFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((uint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((uint)0x00000001, AdditionOperatorsHelper.op_Addition((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, AdditionOperatorsHelper.op_Addition((uint)0x00000001, 1)); + Assert.Equal((uint)0x80000000, AdditionOperatorsHelper.op_Addition((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, AdditionOperatorsHelper.op_Addition((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000000, AdditionOperatorsHelper.op_Addition((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.LeadingZeroCount((uint)0x00000000)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.LeadingZeroCount((uint)0x00000001)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.LeadingZeroCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.LeadingZeroCount((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.LeadingZeroCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.PopCount((uint)0x00000000)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.PopCount((uint)0x00000001)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.PopCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.PopCount((uint)0x80000000)); + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.PopCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.RotateLeft((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, BinaryIntegerHelper.RotateLeft((uint)0x00000001, 1)); + Assert.Equal((uint)0xFFFFFFFE, BinaryIntegerHelper.RotateLeft((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.RotateLeft((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BinaryIntegerHelper.RotateLeft((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.RotateRight((uint)0x00000000, 1)); + Assert.Equal((uint)0x80000000, BinaryIntegerHelper.RotateRight((uint)0x00000001, 1)); + Assert.Equal((uint)0xBFFFFFFF, BinaryIntegerHelper.RotateRight((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x40000000, BinaryIntegerHelper.RotateRight((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BinaryIntegerHelper.RotateRight((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((uint)0x00000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0x00000001)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((uint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((uint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((uint)0x7FFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((uint)0x80000000)); + Assert.False(BinaryNumberHelper.IsPow2((uint)0xFFFFFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((uint)0x00000000, BinaryNumberHelper.Log2((uint)0x00000000)); + Assert.Equal((uint)0x00000000, BinaryNumberHelper.Log2((uint)0x00000001)); + Assert.Equal((uint)0x0000001E, BinaryNumberHelper.Log2((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x0000001F, BinaryNumberHelper.Log2((uint)0x80000000)); + Assert.Equal((uint)0x0000001F, BinaryNumberHelper.Log2((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x00000001, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((uint)0xFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((uint)0x00000000)); + Assert.Equal((uint)0xFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((uint)0x00000001)); + Assert.Equal((uint)0x80000000, BitwiseOperatorsHelper.op_OnesComplement((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((uint)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x80000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x80000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((uint)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((uint)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0x80000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x80000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((uint)0xFFFFFFFF, DecrementOperatorsHelper.op_Decrement((uint)0x00000000)); + Assert.Equal((uint)0x00000000, DecrementOperatorsHelper.op_Decrement((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFE, DecrementOperatorsHelper.op_Decrement((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((uint)0x00000000, DivisionOperatorsHelper.op_Division((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000000, DivisionOperatorsHelper.op_Division((uint)0x00000001, 2)); + Assert.Equal((uint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x40000000, DivisionOperatorsHelper.op_Division((uint)0x80000000, 2)); + Assert.Equal((uint)0x7FFFFFFF, DivisionOperatorsHelper.op_Division((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x00000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((uint)0x00000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x7FFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x80000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x00000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((uint)0x00000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x7FFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x80000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((uint)0x00000001, IncrementOperatorsHelper.op_Increment((uint)0x00000000)); + Assert.Equal((uint)0x00000002, IncrementOperatorsHelper.op_Increment((uint)0x00000001)); + Assert.Equal((uint)0x80000000, IncrementOperatorsHelper.op_Increment((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000001, IncrementOperatorsHelper.op_Increment((uint)0x80000000)); + Assert.Equal((uint)0x00000000, IncrementOperatorsHelper.op_Increment((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((uint)0x00000000, ModulusOperatorsHelper.op_Modulus((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0x00000001, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x00000000, ModulusOperatorsHelper.op_Modulus((uint)0x80000000, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((uint)0x00000000, MultiplyOperatorsHelper.op_Multiply((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000002, MultiplyOperatorsHelper.op_Multiply((uint)0x00000001, 2)); + Assert.Equal((uint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x00000000, MultiplyOperatorsHelper.op_Multiply((uint)0x80000000, 2)); + Assert.Equal((uint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Abs((uint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Abs((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Abs((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Abs((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Abs((uint)0xFFFFFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.Clamp((uint)0x00000000, 0x0001, 0x003F)); + Assert.Equal((uint)0x00000001, NumberHelper.Clamp((uint)0x00000001, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0x7FFFFFFF, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0x80000000, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0xFFFFFFFF, 0x0001, 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Create(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((uint)0xFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((uint)0xFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((uint)0x00000000, (uint)0x00000000), NumberHelper.DivRem((uint)0x00000000, 2)); + Assert.Equal(((uint)0x00000000, (uint)0x00000001), NumberHelper.DivRem((uint)0x00000001, 2)); + Assert.Equal(((uint)0x3FFFFFFF, (uint)0x00000001), NumberHelper.DivRem((uint)0x7FFFFFFF, 2)); + Assert.Equal(((uint)0x40000000, (uint)0x00000000), NumberHelper.DivRem((uint)0x80000000, 2)); + Assert.Equal(((uint)0x7FFFFFFF, (uint)0x00000001), NumberHelper.DivRem((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.Max((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Max((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Max((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000000, NumberHelper.Max((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Max((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Min((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x00000001, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Sign((uint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x00000001)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x80000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0xFFFFFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((uint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((uint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((uint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((uint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((uint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + uint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((uint)0x0000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((uint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((uint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((uint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((uint)0xFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + uint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((uint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((uint)0xFFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_LeftShift((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, ShiftOperatorsHelper.op_LeftShift((uint)0x00000001, 1)); + Assert.Equal((uint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_LeftShift((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_RightShift((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_RightShift((uint)0x00000001, 1)); + Assert.Equal((uint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x40000000, ShiftOperatorsHelper.op_RightShift((uint)0x80000000, 1)); + Assert.Equal((uint)0x7FFFFFFF, ShiftOperatorsHelper.op_RightShift((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((uint)0xFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((uint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x00000000)); + Assert.Equal((uint)0xFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x00000001)); + Assert.Equal((uint)0x80000001, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x80000000)); + Assert.Equal((uint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((uint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x00000000)); + Assert.Equal((uint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0xFFFFFFFF)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Valid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, uint expected) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Invalid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, uint expected) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Invalid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(uint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(uint), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs new file mode 100644 index 0000000000000..1b7ee209a9f4b --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt64Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((ulong)0x0000000000000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((ulong)0x0000000000000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((ulong)0x0000000000000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((ulong)0x0000000000000001, AdditionOperatorsHelper.op_Addition((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, AdditionOperatorsHelper.op_Addition((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x8000000000000000, AdditionOperatorsHelper.op_Addition((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, AdditionOperatorsHelper.op_Addition((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, AdditionOperatorsHelper.op_Addition((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.LeadingZeroCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.LeadingZeroCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.PopCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.PopCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.PopCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.PopCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.PopCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.RotateLeft((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, BinaryIntegerHelper.RotateLeft((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BinaryIntegerHelper.RotateLeft((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.RotateLeft((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateLeft((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x8000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xBFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateRight((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x4000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateRight((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.TrailingZeroCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.TrailingZeroCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((ulong)0x0000000000000000)); + Assert.True(BinaryNumberHelper.IsPow2((ulong)0x0000000000000001)); + Assert.False(BinaryNumberHelper.IsPow2((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((ulong)0x8000000000000000)); + Assert.False(BinaryNumberHelper.IsPow2((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((ulong)0x0000000000000000, BinaryNumberHelper.Log2((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryNumberHelper.Log2((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x000000000000003E, BinaryNumberHelper.Log2((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x000000000000003F, BinaryNumberHelper.Log2((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x000000000000003F, BinaryNumberHelper.Log2((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x0000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000000, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_OnesComplement((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((ulong)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x8000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x8000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x8000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x8000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, DecrementOperatorsHelper.op_Decrement((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((ulong)0x0000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0x3FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x4000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x0000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((ulong)0x0000000000000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x8000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x0000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((ulong)0x0000000000000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x8000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((ulong)0x0000000000000001, IncrementOperatorsHelper.op_Increment((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000002, IncrementOperatorsHelper.op_Increment((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000000, IncrementOperatorsHelper.op_Increment((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000001, IncrementOperatorsHelper.op_Increment((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, IncrementOperatorsHelper.op_Increment((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((ulong)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((ulong)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000002, MultiplyOperatorsHelper.op_Multiply((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Abs((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Abs((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Abs((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Abs((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Abs((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Clamp((ulong)0x0000000000000000, 0x0001, 0x003F)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Clamp((ulong)0x0000000000000001, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0x7FFFFFFFFFFFFFFF, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0x8000000000000000, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0xFFFFFFFFFFFFFFFF, 0x0001, 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.Create(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.Create(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.Create(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Create(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ulong)0xFFFFFFFFFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ulong)0xFFFFFFFF80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((ulong)0xFFFFFFFF80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((ulong)0x0000000000000000, (ulong)0x0000000000000000), NumberHelper.DivRem((ulong)0x0000000000000000, 2)); + Assert.Equal(((ulong)0x0000000000000000, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0x0000000000000001, 2)); + Assert.Equal(((ulong)0x3FFFFFFFFFFFFFFF, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal(((ulong)0x4000000000000000, (ulong)0x0000000000000000), NumberHelper.DivRem((ulong)0x8000000000000000, 2)); + Assert.Equal(((ulong)0x7FFFFFFFFFFFFFFF, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Max((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Max((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Max((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Max((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Max((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Min((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Sign((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ulong)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((ulong)0x0000000000000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((ulong)0x00000000000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((ulong)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((ulong)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + ulong result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ulong)0x000000000000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((ulong)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((ulong)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((ulong)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((ulong)0x00000000FFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((ulong)0x8000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + ulong result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((ulong)0x8000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((ulong)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x00000000FFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, ShiftOperatorsHelper.op_LeftShift((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x3FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x4000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, SubtractionOperatorsHelper.op_Subtraction((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((ulong)0x0000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x0000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((ulong)0x0000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Valid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, ulong expected) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Invalid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, ulong expected) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Invalid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(ulong), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(ulong), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs new file mode 100644 index 0000000000000..8e0c92c2a82c1 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs @@ -0,0 +1,1695 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UIntPtrTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((nuint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((nuint)0x00000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), MinMaxValueHelper.MaxValue); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, MinMaxValueHelper.MaxValue); + } + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((nuint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((nuint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, AdditionOperatorsHelper.op_Addition((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000002, AdditionOperatorsHelper.op_Addition((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x80000000, AdditionOperatorsHelper.op_Addition((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, AdditionOperatorsHelper.op_Addition((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, AdditionOperatorsHelper.op_Addition((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void LeadingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x0000000000000020, BinaryIntegerHelper.LeadingZeroCount((nuint)0x00000000)); + Assert.Equal((nuint)0x000000000000001F, BinaryIntegerHelper.LeadingZeroCount((nuint)0x00000001)); + Assert.Equal((nuint)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((nuint)0x80000000)); + Assert.Equal((nuint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void PopCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.PopCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.PopCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.PopCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.PopCount((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.PopCount((nuint)0x00000001)); + Assert.Equal((nuint)0x0000001F, BinaryIntegerHelper.PopCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.PopCount((nuint)0x80000000)); + Assert.Equal((nuint)0x00000020, BinaryIntegerHelper.PopCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void RotateLeftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.RotateLeft((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000002, BinaryIntegerHelper.RotateLeft((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xFFFFFFFE, BinaryIntegerHelper.RotateLeft((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.RotateLeft((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFF, BinaryIntegerHelper.RotateLeft((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void RotateRightTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x4000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.RotateRight((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x80000000, BinaryIntegerHelper.RotateRight((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xBFFFFFFF, BinaryIntegerHelper.RotateRight((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x40000000, BinaryIntegerHelper.RotateRight((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFF, BinaryIntegerHelper.RotateRight((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void TrailingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0x00000001)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void IsPow2Test() + { + if (Environment.Is64BitProcess) + { + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0x0000000000000000))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nuint)0x0000000000000001))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nuint)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.False(BinaryNumberHelper.IsPow2((nuint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((nuint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((nuint)0x7FFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((nuint)0x80000000)); + Assert.False(BinaryNumberHelper.IsPow2((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void Log2Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x000000000000003E), BinaryNumberHelper.Log2(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryNumberHelper.Log2(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryNumberHelper.Log2(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryNumberHelper.Log2((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, BinaryNumberHelper.Log2((nuint)0x00000001)); + Assert.Equal((nuint)0x0000001E, BinaryNumberHelper.Log2((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000001F, BinaryNumberHelper.Log2((nuint)0x80000000)); + Assert.Equal((nuint)0x0000001F, BinaryNumberHelper.Log2((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_BitwiseAndTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_BitwiseOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_ExclusiveOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_OnesComplementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x00000000)); + Assert.Equal((nuint)0xFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x00000001)); + Assert.Equal((nuint)0x80000000, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_LessThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((nuint)0x00000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x00000001, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x80000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x00000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x00000001, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x80000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_GreaterThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x00000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x00000001, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x80000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x00000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x00000001, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x80000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_DecrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, DecrementOperatorsHelper.op_Decrement((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, DecrementOperatorsHelper.op_Decrement((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFE, DecrementOperatorsHelper.op_Decrement((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_DivisionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0x3FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x4000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, DivisionOperatorsHelper.op_Division((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000000, DivisionOperatorsHelper.op_Division((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x40000000, DivisionOperatorsHelper.op_Division((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0x7FFFFFFF, DivisionOperatorsHelper.op_Division((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void op_EqualityTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x00000000, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((nuint)0x00000001, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x80000000, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_InequalityTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x00000000, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((nuint)0x00000001, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x80000000, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_IncrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000002), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000001, IncrementOperatorsHelper.op_Increment((nuint)0x00000000)); + Assert.Equal((nuint)0x00000002, IncrementOperatorsHelper.op_Increment((nuint)0x00000001)); + Assert.Equal((nuint)0x80000000, IncrementOperatorsHelper.op_Increment((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000001, IncrementOperatorsHelper.op_Increment((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, IncrementOperatorsHelper.op_Increment((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_ModulusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, ModulusOperatorsHelper.op_Modulus((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x00000000, ModulusOperatorsHelper.op_Modulus((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void op_MultiplyTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000002), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000002, MultiplyOperatorsHelper.op_Multiply((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void AbsTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Abs(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Abs(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Abs(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Abs((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Abs((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Abs((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Abs((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Abs((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void ClampTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Clamp(unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Clamp(unchecked((nuint)0x0000000000000001), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0x7FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0x8000000000000000), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0xFFFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + } + else + { + Assert.Equal((nuint)0x00000001, NumberHelper.Clamp((nuint)0x00000000, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x00000001, NumberHelper.Clamp((nuint)0x00000001, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0x7FFFFFFF, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0x80000000, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0xFFFFFFFF, (nuint)0x00000001, (nuint)0x0000003F)); + } + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Create(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Create(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((nuint)0x0000000000007FFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nuint)0xFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((nuint)0x000000007FFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nuint)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x00)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x01)); + Assert.Equal(unchecked((nuint)0x000000000000007F), NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nuint)0xFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal((unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal((unchecked((nuint)0x3FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal((unchecked((nuint)0x4000000000000000), unchecked((nuint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal((unchecked((nuint)0x7FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal(((nuint)0x00000000, (nuint)0x00000000), NumberHelper.DivRem((nuint)0x00000000, (nuint)2)); + Assert.Equal(((nuint)0x00000000, (nuint)0x00000001), NumberHelper.DivRem((nuint)0x00000001, (nuint)2)); + Assert.Equal(((nuint)0x3FFFFFFF, (nuint)0x00000001), NumberHelper.DivRem((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal(((nuint)0x40000000, (nuint)0x00000000), NumberHelper.DivRem((nuint)0x80000000, (nuint)2)); + Assert.Equal(((nuint)0x7FFFFFFF, (nuint)0x00000001), NumberHelper.DivRem((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void MaxTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Max(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Max(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Max(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, NumberHelper.Max((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Max((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Max((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000000, NumberHelper.Max((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Max((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void MinTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Min(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Min((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void SignTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Sign(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Sign((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x00000001)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x80000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nuint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((nuint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((nuint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((nuint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((nuint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nuint)0x0000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((nuint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((nuint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((nuint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((nuint)0xFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nuint)0x00000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((nuint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0xFFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000002, ShiftOperatorsHelper.op_LeftShift((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void op_RightShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0x3FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x4000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_RightShift((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_RightShift((nuint)0x00000001, 1)); + Assert.Equal((nuint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x40000000, ShiftOperatorsHelper.op_RightShift((nuint)0x80000000, 1)); + Assert.Equal((nuint)0x7FFFFFFF, ShiftOperatorsHelper.op_RightShift((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void op_SubtractionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_UnaryNegationTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x00000000)); + Assert.Equal((nuint)0xFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x00000001)); + Assert.Equal((nuint)0x80000001, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x80000000)); + Assert.Equal((nuint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_UnaryPlusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0xFFFFFFFF)); + } + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Valid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, nuint expected) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Invalid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, nuint expected) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Invalid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(nuint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(nuint), result); + } + } +} From a31d0d57eff985a68828c37775d103b91032f786 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 11 Jul 2021 14:13:35 -0400 Subject: [PATCH 71/72] Move PosixSignalRegistration to corelib (#55433) * Move PosixSignalRegistration to corelib * Fix trimming issue on mobile targets --- .../TestUtilities/System/PlatformDetection.cs | 3 +- .../System.Private.CoreLib.Shared.projitems | 15 +++++++++ .../Runtime/InteropServices/PosixSignal.cs | 0 .../InteropServices/PosixSignalContext.cs | 0 ...ignalRegistration.PlatformNotSupported.cs} | 3 ++ .../PosixSignalRegistration.Unix.cs | 0 .../PosixSignalRegistration.Windows.cs | 0 .../PosixSignalRegistration.cs | 0 .../src/Resources/Strings.resx | 3 -- .../src/System.Runtime.InteropServices.csproj | 27 ++-------------- ...ystem.Runtime.InteropServices.Tests.csproj | 5 +-- .../PosixSignalRegistrationTests.Browser.cs | 29 ----------------- .../PosixSignalRegistrationTests.Unix.cs | 32 +++++++++++++------ 13 files changed, 46 insertions(+), 71 deletions(-) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignal.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalContext.cs (100%) rename src/libraries/{System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs => System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs} (80%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.cs (100%) delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index e11da7e8d4d93..90f21d02f5c49 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -37,7 +37,8 @@ public static partial class PlatformDetection public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS")); public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); public static bool IsNotBrowser => !IsBrowser; - public static bool IsNotMobile => IsNotBrowser && !IsMacCatalyst && !IsiOS && !IstvOS && !IsAndroid; + public static bool IsMobile => IsBrowser || IsMacCatalyst || IsiOS || IstvOS || IsAndroid; + public static bool IsNotMobile => !IsMobile; public static bool IsNotNetFramework => !IsNetFramework; public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a1e270dbde97d..3dcfc2c7313a5 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -837,6 +837,9 @@ + + + @@ -1235,6 +1238,7 @@ + @@ -1651,6 +1655,9 @@ Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs + + Common\Interop\Windows\Interop.SetConsoleCtrlHandler.cs + Common\Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs @@ -1833,6 +1840,7 @@ + @@ -2129,6 +2137,10 @@ Common\Interop\Unix\System.Native\Interop.GetPid.cs + + + + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignal.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignal.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalContext.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalContext.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs similarity index 80% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs index 4bfd2eae0be67..3ccc169b1a84b 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs @@ -1,12 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Runtime.InteropServices { public sealed partial class PosixSignalRegistration { private PosixSignalRegistration() { } + [DynamicDependency("#ctor")] // Prevent the private ctor and the IDisposable implementation from getting linked away public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) { if (handler is null) diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index d7644789f28c2..08d7e045a580f 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -111,7 +111,4 @@ Specified file length was too large for the file system. - - Cannot create '{0}' because a file or directory with the same name already exists. - diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index 7137155589b23..3bc59997f7f0c 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -2,7 +2,7 @@ true enable - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent) @@ -33,9 +33,6 @@ - - - @@ -51,27 +48,7 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 33204d0ee7cc9..f0ffe70d47c31 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -1,7 +1,7 @@ true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix true true @@ -192,7 +192,4 @@ - - - diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs deleted file mode 100644 index 2202ed25744ee..0000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Linq; -using System.Runtime.InteropServices; -using System.Collections.Generic; - -namespace System.Tests -{ - public partial class PosixSignalRegistrationTests - { - public static IEnumerable UninstallableSignals() => Enumerable.Empty(); - - public static IEnumerable SupportedSignals() => Enumerable.Empty(); - - public static IEnumerable UnsupportedSignals() - { - foreach (PosixSignal signal in Enum.GetValues()) - { - yield return new object[] { signal }; - } - - yield return new object[] { 0 }; - yield return new object[] { 3 }; - yield return new object[] { -1000 }; - yield return new object[] { 1000 }; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs index 1716a3ba24f7f..e08d77565513a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -14,23 +14,37 @@ public partial class PosixSignalRegistrationTests { public static IEnumerable UninstallableSignals() { - yield return new object[] { (PosixSignal)9 }; + if (PlatformDetection.IsNotMobile) + { + yield return new object[] { (PosixSignal)9 }; + } } public static IEnumerable SupportedSignals() { - foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) - yield return new object[] { value }; + if (PlatformDetection.IsNotMobile) + { + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; + } } public static IEnumerable UnsupportedSignals() { + if (PlatformDetection.IsMobile) + { + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; + } + yield return new object[] { 0 }; yield return new object[] { -1000 }; yield return new object[] { 1000 }; } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static bool NotMobileAndRemoteExecutable => PlatformDetection.IsNotMobile && RemoteExecutor.IsSupported; + + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [MemberData(nameof(SupportedSignals))] public void SignalHandlerCalledForKnownSignals(PosixSignal s) { @@ -55,7 +69,7 @@ public void SignalHandlerCalledForKnownSignals(PosixSignal s) }, s.ToString()).Dispose(); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [MemberData(nameof(PosixSignalAsRawValues))] public void SignalHandlerCalledForRawSignals(PosixSignal s) { @@ -80,7 +94,7 @@ public void SignalHandlerCalledForRawSignals(PosixSignal s) }, s.ToString()).Dispose(); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerWorksForSecondRegistration() { PosixSignal signal = PosixSignal.SIGCONT; @@ -104,7 +118,7 @@ public void SignalHandlerWorksForSecondRegistration() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerNotCalledWhenDisposed() { PosixSignal signal = PosixSignal.SIGCONT; @@ -118,7 +132,7 @@ public void SignalHandlerNotCalledWhenDisposed() Thread.Sleep(100); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerNotCalledWhenFinalized() { PosixSignal signal = PosixSignal.SIGCONT; @@ -140,7 +154,7 @@ void CreateDanglingRegistration() } } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [InlineData(PosixSignal.SIGINT, true, 0)] [InlineData(PosixSignal.SIGINT, false, 130)] [InlineData(PosixSignal.SIGTERM, true, 0)] From 7d518fe3d0963f9f2cbe7aa971df200d2f8e67c7 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sun, 11 Jul 2021 15:59:37 -0400 Subject: [PATCH 72/72] [mono] Improve aot code generation for isinst for sealed classes. (#55450) Generate the same code as in the non-aot case, i.e. compare the vtable with a constant. --- src/mono/mono/mini/type-checking.c | 46 ++++++++++++------------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/mono/mono/mini/type-checking.c b/src/mono/mono/mini/type-checking.c index 8f6f93e1f3561..b623c603741af 100644 --- a/src/mono/mono/mini/type-checking.c +++ b/src/mono/mono/mini/type-checking.c @@ -429,15 +429,11 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - if (!m_class_get_rank (klass) && !cfg->compile_aot && mono_class_is_sealed (klass)) { - /* the remoting code is broken, access the class for now */ - if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/ - MonoVTable *vt = mono_class_vtable_checked (klass, cfg->error); - if (!is_ok (cfg->error)) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt); + if (!m_class_get_rank (klass) && mono_class_is_sealed (klass)) { + if (cfg->compile_aot) { + MonoInst *vtable_ins; + EMIT_NEW_AOTCONST (cfg, vtable_ins, MONO_PATCH_INFO_VTABLE, klass); + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg); } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); @@ -584,28 +580,22 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_us MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); /* the is_null_bb target simply copies the input register to the output */ mini_emit_isninst_cast (cfg, klass_reg, m_class_get_cast_class (klass), false_bb, is_null_bb); - } else { - if (!cfg->compile_aot && mono_class_is_sealed (klass)) { - g_assert (!context_used); - /* the remoting code is broken, access the class for now */ - if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/ - MonoVTable *vt = mono_class_vtable_checked (klass, cfg->error); - if (!is_ok (cfg->error)) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); - } - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb); + } else if (mono_class_is_sealed (klass)) { + g_assert (!context_used); + if (cfg->compile_aot) { + MonoInst *vtable_ins; + EMIT_NEW_AOTCONST (cfg, vtable_ins, MONO_PATCH_INFO_VTABLE, klass); + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg); } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - /* the is_null_bb target simply copies the input register to the output */ - mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); } + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb); + } else { + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); + /* the is_null_bb target simply copies the input register to the output */ + mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); } }