diff --git a/Directory.Build.props b/Directory.Build.props index d957a798303ad..541ad0417e40d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,13 +10,15 @@ - OSX - FreeBSD - NetBSD - illumos - Solaris - Linux - windows + <_hostOS>Linux + <_hostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">OSX + <_hostOS Condition="$([MSBuild]::IsOSPlatform('FREEBSD'))">FreeBSD + <_hostOS Condition="$([MSBuild]::IsOSPlatform('NETBSD'))">NetBSD + <_hostOS Condition="$([MSBuild]::IsOSPlatform('ILLUMOS'))">illumos + <_hostOS Condition="$([MSBuild]::IsOSPlatform('SOLARIS'))">Solaris + <_hostOS Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">windows + <_hostOS Condition="'$(TargetOS)' == 'Browser'">Browser + $(_hostOS) true @@ -115,16 +117,13 @@ the build system to use browser/ios/android as the _runtimeOS for produced package RIDs. --> <_runtimeOS Condition="'$(TargetsMobile)' == 'true'">$(TargetOS.ToLowerInvariant()) - <_runtimeOSVersionIndex>$(_runtimeOS.IndexOfAny(".-0123456789")) - <_runtimeOSFamily Condition="'$(_runtimeOSVersionIndex)' != '-1'">$(_runtimeOS.SubString(0, $(_runtimeOSVersionIndex))) - <_portableOS>linux <_portableOS Condition="'$(_runtimeOS)' == 'linux-musl'">linux-musl - <_portableOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx - <_portableOS Condition="'$(_runtimeOSFamily)' == 'win' or '$(_runtimeOS)' == 'win' or '$(TargetOS)' == 'windows'">win - <_portableOS Condition="'$(_runtimeOSFamily)' == 'FreeBSD'">freebsd - <_portableOS Condition="'$(_runtimeOSFamily)' == 'illumos'">illumos - <_portableOS Condition="'$(_runtimeOSFamily)' == 'Solaris'">solaris + <_portableOS Condition="'$(_hostOS)' == 'OSX'">osx + <_portableOS Condition="'$(_runtimeOS)' == 'win' or '$(TargetOS)' == 'windows'">win + <_portableOS Condition="'$(_runtimeOS)' == 'FreeBSD' or '$(TargetOS)' == 'FreeBSD'">freebsd + <_portableOS Condition="'$(_runtimeOS)' == 'illumos' or '$(TargetOS)' == 'illumos'">illumos + <_portableOS Condition="'$(_runtimeOS)' == 'Solaris' or '$(TargetOS)' == 'Solaris'">solaris <_portableOS Condition="'$(_runtimeOS)' == 'Browser'">browser <_portableOS Condition="'$(_runtimeOS)' == 'maccatalyst'">maccatalyst <_portableOS Condition="'$(_runtimeOS)' == 'ios'">ios @@ -135,16 +134,12 @@ <_runtimeOS Condition="$(_runtimeOS.StartsWith('tizen'))">linux <_runtimeOS Condition="'$(PortableBuild)' == 'true'">$(_portableOS) - - - <_portableOS Condition="'$(TargetOS)' == 'Unix' and '$(_runtimeOSFamily)' != 'osx' and '$(_runtimeOSFamily)' != 'FreeBSD' and '$(_runtimeOS)' != 'linux-musl' and '$(_runtimeOSFamily)' != 'illumos' and '$(_runtimeOSFamily)' != 'Solaris'">linux + <_toolRuntimeRID Condition="'$(CrossBuild)' == 'true'">$(_hostOS.ToLowerInvariant)-$(_hostArch) <_toolRuntimeRID Condition="'$(BuildingInsideVisualStudio)' == 'true'">$(_runtimeOS)-x64 <_toolRuntimeRID Condition="'$(_toolRuntimeRID)' == ''">$(_runtimeOS)-$(_hostArch) - - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'linux-musl' and $(TargetArchitecture.StartsWith('arm')) and !$(_hostArch.StartsWith('arm'))">linux-x64 <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser'">linux-x64 @@ -164,26 +159,12 @@ $(_toolRuntimeRID) <_packageRID Condition="'$(PortableBuild)' == 'true'">$(_portableOS)-$(TargetArchitecture) + <_packageRID Condition="'$(CrossBuild)' == 'true'">$(_hostOS.ToLowerInvariant)-$(TargetArchitecture) $(_packageRID) $(_runtimeOS)-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'windows'">win-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'OSX'">osx-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Linux'">linux-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'FreeBSD'">freebsd-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'NetBSD'">netbsd-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'illumos'">illumos-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Solaris'">solaris-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'MacCatalyst'">maccatalyst-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'iOS'">ios-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'iOSSimulator'">iossimulator-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'tvOS'">tvos-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'tvOSSimulator'">tvossimulator-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Android'">android-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Browser'">browser-$(TargetArchitecture) - $(PackageRID) - $(_outputRID) + $(_portableOS)-$(TargetArchitecture) diff --git a/Directory.Build.targets b/Directory.Build.targets index 0460647e6de5d..e390cc3dda320 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -54,7 +54,9 @@ $(RuntimePackageDisclaimer) $(PackageDescription) + $(BeforePack);AddNETStandardCompatErrorFileForPackaging + AddNETStandardCompatErrorFileForPackaging;$(GenerateNuspecDependsOn) @@ -76,8 +78,7 @@ + Outputs="unused"> <_NETStandardCompatErrorFilePath>$(BaseIntermediateOutputPath)netstandardcompaterrors\%(NETStandardCompatError.Identity)\$(PackageId).targets <_NETStandardCompatErrorFileTarget>NETStandardCompatError_$(PackageId.Replace('.', '_'))_$([System.String]::new('%(NETStandardCompatError.Supported)').Replace('.', '_')) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 00d781b1461fe..8301d951525f2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,7 +8,7 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk f5349765b7af1970c5b25cce4ed278544907cbe0 diff --git a/eng/Versions.props b/eng/Versions.props index cdb313d0a0654..5240e72cbb270 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -180,7 +180,7 @@ 11.1.0-alpha.1.21328.1 11.1.0-alpha.1.21328.1 - 6.0.0-preview.7.21330.1 - $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) + 6.0.0-preview.7.21330.1 + $(MicrosoftNETWorkloadEmscriptenManifest60100) diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 0db890a8e572d..6fc0b85de73dd 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -237,6 +237,22 @@ jobs: logicalmachine: 'perfpixel4a' iosLlvmBuild: False + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Windows_x64 + jobParameters: + testGroup: perf + runtimeType: iOSMono + projectFile: ios_scenarios.proj + runKind: ios_scenarios + runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml + logicalmachine: 'perfpixel4a' + iosLlvmBuild: True + # run mono microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml index 6bfd8637bfac5..65b6226093a10 100644 --- a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -44,6 +44,29 @@ steps: archiveExtension: '.tar.gz' archiveType: tar tarCompression: gz + - script: rm -r -f $(Build.SourcesDirectory)/src/mono/sample/iOS/bin + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS + displayName: Clean bindir + - script: make build-appbundle TARGET=iOS MONO_ARCH=arm64 MONO_CONFIG=Release AOT=True USE_LLVM=True + env: + DevTeamProvisioning: '-' + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS + displayName: Build HelloiOS AOT sample app LLVM=True + - task: PublishBuildArtifacts@1 + condition: succeededOrFailed() + displayName: 'Publish binlog' + inputs: + pathtoPublish: $(Build.SourcesDirectory)/src/mono/sample/iOS/msbuild.binlog + artifactName: ${{ parameters.artifactName }} + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app + includeRootFolder: true + displayName: iOS Sample App LLVM + artifactName: iOSSampleAppLLVM + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz - template: /eng/pipelines/common/upload-artifact-step.yml parameters: diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 3dee80bb370d2..59b7843925e04 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -165,7 +165,13 @@ jobs: artifactFileName: 'iOSSampleAppNoLLVM.tar.gz' artifactName: 'iOSSampleAppNoLLVM' displayName: 'iOS Sample App NoLLVM' - + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory)/iosHelloWorld/llvm + cleanUnpackFolder: false + artifactFileName: 'iOSSampleAppLLVM.tar.gz' + artifactName: 'iOSSampleAppLLVM' + displayName: 'iOS Sample App LLVM' # Create Core_Root - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(buildConfig) $(archType) generatelayoutonly $(librariesOverrideArg) diff --git a/eng/pipelines/coreclr/templates/run-performance-job.yml b/eng/pipelines/coreclr/templates/run-performance-job.yml index e031d0fa1ce16..de523bb2d99e6 100644 --- a/eng/pipelines/coreclr/templates/run-performance-job.yml +++ b/eng/pipelines/coreclr/templates/run-performance-job.yml @@ -23,7 +23,6 @@ parameters: runKind: '' # required -- test category logicalMachine: '' # required -- Used to specify a which pool of machines the test should run against javascriptEngine: 'NoJS' - iosLlvmBuild: 'False' jobs: - template: xplat-pipeline-job.yml @@ -143,6 +142,6 @@ jobs: displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}_${{ parameters.iosLlvmBuild }}' + artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}' continueOnError: true condition: always() diff --git a/eng/pipelines/coreclr/templates/run-scenarios-job.yml b/eng/pipelines/coreclr/templates/run-scenarios-job.yml index 075807453ec3d..2fa6822205aa0 100644 --- a/eng/pipelines/coreclr/templates/run-scenarios-job.yml +++ b/eng/pipelines/coreclr/templates/run-scenarios-job.yml @@ -157,6 +157,6 @@ jobs: displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}' + artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_$(iOSLlvmBuild)' continueOnError: true condition: always() diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index 03aff42d25f78..078cad080a4b9 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -152,7 +152,12 @@ if ($iOSMono) { { mkdir $WorkItemDirectory } - Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + if($iOSLlvmBuild) { + Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse + } else { + Copy-Item -path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + } + $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' } diff --git a/src/coreclr/.nuget/Directory.Build.props b/src/coreclr/.nuget/Directory.Build.props index c8c468ebc609c..cff00f49e0e56 100644 --- a/src/coreclr/.nuget/Directory.Build.props +++ b/src/coreclr/.nuget/Directory.Build.props @@ -119,8 +119,8 @@ - - + amd64 $(TargetArchitecture) diff --git a/src/coreclr/.nuget/builds.targets b/src/coreclr/.nuget/builds.targets index afab9019e11dc..4dd4d825baeb9 100644 --- a/src/coreclr/.nuget/builds.targets +++ b/src/coreclr/.nuget/builds.targets @@ -7,11 +7,11 @@ - + - <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(PackageRID)'" /> + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(OutputRid)'" /> @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 86dd19a5a7560..47b1bd454d80e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -450,6 +450,7 @@ internal static bool PerformRuntimeSpecificGateActivities(int cpuUtilization) private static extern unsafe bool PostQueuedCompletionStatus(NativeOverlapped* overlapped); [CLSCompliant(false)] + [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => PostQueuedCompletionStatus(overlapped); @@ -547,6 +548,7 @@ RegisteredWaitHandle registeredWaitHandle ); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [SupportedOSPlatform("windows")] public static bool BindHandle(IntPtr osHandle) { return BindIOCompletionCallbackNative(osHandle); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 955a7bba1e9a9..35b01ad27ec43 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14458,46 +14458,26 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) break; case GT_CAST: + f1 = forceCastToFloat(d1); - if (tree->gtOverflow() && - ((op1->TypeIs(TYP_DOUBLE) && CheckedOps::CastFromDoubleOverflows(d1, tree->CastToType())) || - (op1->TypeIs(TYP_FLOAT) && - CheckedOps::CastFromFloatOverflows(forceCastToFloat(d1), tree->CastToType())))) + if ((op1->TypeIs(TYP_DOUBLE) && CheckedOps::CastFromDoubleOverflows(d1, tree->CastToType())) || + (op1->TypeIs(TYP_FLOAT) && CheckedOps::CastFromFloatOverflows(f1, tree->CastToType()))) { - return tree; - } + // The conversion overflows. The ECMA spec says, in III 3.27, that + // "...if overflow occurs converting a floating point type to an integer, ..., + // the value returned is unspecified." However, it would at least be + // desirable to have the same value returned for casting an overflowing + // constant to an int as would be obtained by passing that constant as + // a parameter and then casting that parameter to an int type. - assert(tree->TypeIs(genActualType(tree->CastToType()))); + // Don't fold overflowing converions, as the value returned by + // JIT's codegen doesn't always match with the C compiler's cast result. + // We want the behavior to be the same with or without folding. - if ((op1->TypeIs(TYP_FLOAT) && !_finite(forceCastToFloat(d1))) || - (op1->TypeIs(TYP_DOUBLE) && !_finite(d1))) - { - // The floating point constant is not finite. The ECMA spec says, in - // III 3.27, that "...if overflow occurs converting a floating point type - // to an integer, ..., the value returned is unspecified." However, it would - // at least be desirable to have the same value returned for casting an overflowing - // constant to an int as would obtained by passing that constant as a parameter - // then casting that parameter to an int type. We will assume that the C compiler's - // cast logic will yield the desired result (and trust testing to tell otherwise). - // Cross-compilation is an issue here; if that becomes an important scenario, we should - // capture the target-specific values of overflow casts to the various integral types as - // constants in a target-specific function. - CLANG_FORMAT_COMMENT_ANCHOR; - - // Don't fold conversions of +inf/-inf to integral value on all platforms - // as the value returned by JIT helper doesn't match with the C compiler's cast result. - // We want the behavior to be same with or without folding. return tree; } - if (d1 <= -1.0 && varTypeIsUnsigned(tree->CastToType())) - { - // Don't fold conversions of these cases becasue the result is unspecified per ECMA spec - // and the native math doing the fold doesn't match the run-time computation on all - // platforms. - // We want the behavior to be same with or without folding. - return tree; - } + assert(tree->TypeIs(genActualType(tree->CastToType()))); switch (tree->CastToType()) { diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index e49ba55eca816..110730f15e6c5 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -3012,7 +3012,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu case TYP_FLOAT: { float arg0Val = GetConstantSingle(arg0VN); - assert(!checkedCast || !CheckedOps::CastFromFloatOverflows(arg0Val, castToType)); + assert(!CheckedOps::CastFromFloatOverflows(arg0Val, castToType)); switch (castToType) { @@ -3054,7 +3054,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu case TYP_DOUBLE: { double arg0Val = GetConstantDouble(arg0VN); - assert(!checkedCast || !CheckedOps::CastFromDoubleOverflows(arg0Val, castToType)); + assert(!CheckedOps::CastFromDoubleOverflows(arg0Val, castToType)); switch (castToType) { @@ -3322,26 +3322,32 @@ bool ValueNumStore::VNEvalShouldFold(var_types typ, VNFunc func, ValueNum arg0VN } } - // Is this a checked cast that will always throw an exception? - if (func == VNF_CastOvf) + // Is this a checked cast that will always throw an exception or one with an implementation-defined result? + if (VNFuncIsNumericCast(func)) { - var_types castToType; - bool fromUnsigned; - GetCastOperFromVN(arg1VN, &castToType, &fromUnsigned); var_types castFromType = TypeOfVN(arg0VN); - switch (castFromType) + // By policy, we do not fold conversions from floating-point types that result in + // overflow, as the value the C++ compiler gives us does not always match our own codegen. + if ((func == VNF_CastOvf) || varTypeIsFloating(castFromType)) { - case TYP_INT: - return !CheckedOps::CastFromIntOverflows(GetConstantInt32(arg0VN), castToType, fromUnsigned); - case TYP_LONG: - return !CheckedOps::CastFromLongOverflows(GetConstantInt64(arg0VN), castToType, fromUnsigned); - case TYP_FLOAT: - return !CheckedOps::CastFromFloatOverflows(GetConstantSingle(arg0VN), castToType); - case TYP_DOUBLE: - return !CheckedOps::CastFromDoubleOverflows(GetConstantDouble(arg0VN), castToType); - default: - return false; + var_types castToType; + bool fromUnsigned; + GetCastOperFromVN(arg1VN, &castToType, &fromUnsigned); + + switch (castFromType) + { + case TYP_INT: + return !CheckedOps::CastFromIntOverflows(GetConstantInt32(arg0VN), castToType, fromUnsigned); + case TYP_LONG: + return !CheckedOps::CastFromLongOverflows(GetConstantInt64(arg0VN), castToType, fromUnsigned); + case TYP_FLOAT: + return !CheckedOps::CastFromFloatOverflows(GetConstantSingle(arg0VN), castToType); + case TYP_DOUBLE: + return !CheckedOps::CastFromDoubleOverflows(GetConstantDouble(arg0VN), castToType); + default: + return false; + } } } diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index c031f303e44e0..56fbb93adb924 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -169,7 +169,7 @@ Instruction set(s) to use for compilation - Instruction set must not be '{0}' for this architecture and operating system + Instruction set '{0}' is not valid for this architecture and operating system Instruction set(s) specified without also specifying input-bubble diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj index be3bd23767f70..4b1fc1af49908 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj @@ -7,7 +7,7 @@ $(TargetArchitecture) arm - $(_runtimeOS)-$(TargetArchitectureAppHost) + $(PackageRID) diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 14bd22538d2b5..ac7be2a94398f 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -5045,7 +5045,7 @@ VOID ETW::InfoLog::RuntimeInformation(INT32 type) { PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""); UINT8 startupMode = 0; - UINT startupFlags = 0; + UINT startupFlags = CorHost2::GetStartupFlags(); PathString dllPath; UINT8 Sku = ETW::InfoLog::InfoStructs::CoreCLR; diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj index 0cd26443fc557..a3f364815d63c 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj @@ -32,6 +32,9 @@ + + + diff --git a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs index c1b00cafd4e6a..3206cd74d1559 100644 --- a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs +++ b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs @@ -71,6 +71,56 @@ public void EnvironmentVariable_ArchSpecificDotnetRootIsUsedOverDotnetRoot() .And.NotHaveStdErrContaining("Using environment variable DOTNET_ROOT="); } + [Fact] + public void EnvironmentVariable_DotnetRootPathDoesNotExist() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (TestOnlyProductBehavior.Enable(appExe)) + { + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot("non_existent_path") + .MultilevelLookup(false) + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, + sharedTestState.InstallLocation) + .Execute() + .Should().Pass() + .And.HaveStdErrContaining("Did not find [DOTNET_ROOT] directory [non_existent_path]") + // If DOTNET_ROOT points to a folder that does not exist, we fall back to the global install path. + .And.HaveUsedGlobalInstallLocation(sharedTestState.InstallLocation) + .And.HaveStdOutContaining("Hello World"); + } + } + + [Fact] + public void EnvironmentVariable_DotnetRootPathExistsButHasNoHost() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var projDir = fixture.TestProject.ProjectDirectory; + using (TestOnlyProductBehavior.Enable(appExe)) + { + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(projDir) + .MultilevelLookup(false) + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, + sharedTestState.InstallLocation) + .Execute() + .Should().Fail() + .And.HaveUsedDotNetRootInstallLocation(projDir, fixture.CurrentRid) + // If DOTNET_ROOT points to a folder that exists we assume that there's a dotnet installation in it + .And.HaveStdErrContaining($"A fatal error occurred. The required library {RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform ("hostfxr")} could not be found."); + } + } + [Fact] [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] public void InstallLocationFile_ArchSpecificLocationIsPickedFirst() @@ -173,6 +223,7 @@ public SharedTestState() PortableAppFixture = fixture; BaseDirectory = Path.GetDirectoryName(PortableAppFixture.SdkDotnet.GreatestVersionHostFxrFilePath); + InstallLocation = fixture.BuiltDotnet.BinPath; } public void Dispose() diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs new file mode 100644 index 0000000000000..a3ff4e14fb7a7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// IStream interface. + /// + /// + /// This interface explicitly doesn't use the built-in COM support, but instead is only used with ComWrappers. + /// + internal interface IStream + { + // pcbRead is optional + unsafe void Read(byte* pv, uint cb, uint* pcbRead); + + // pcbWritten is optional + unsafe void Write(byte* pv, uint cb, uint* pcbWritten); + + // SeekOrgin matches the native values, plibNewPosition is optional + unsafe void Seek(long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition); + + void SetSize(ulong libNewSize); + + // pcbRead and pcbWritten are optional + unsafe HRESULT CopyTo( + IntPtr pstm, + ulong cb, + ulong* pcbRead, + ulong* pcbWritten); + + void Commit(uint grfCommitFlags); + + void Revert(); + + HRESULT LockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + HRESULT UnlockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + unsafe void Stat( + STATSTG* pstatstg, + STATFLAG grfStatFlag); + + unsafe HRESULT Clone(IntPtr* ppstm); + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs similarity index 97% rename from src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs rename to src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs index 6fdf6718f050a..b8907e5a33a9f 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Ole32 { /// - /// COM IStream interface. + /// COM IStream interface. /// /// /// The definition in does not lend diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs index ecff49e72016a..f41e4f48df578 100644 --- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs @@ -11,6 +11,7 @@ using System.Net.Sockets; using System.Net.WebSockets; using System.Threading; +using System.Diagnostics; namespace System.Net.Test.Common { @@ -20,7 +21,7 @@ namespace System.Net.Test.Common public abstract class LoopbackServerFactory { public abstract GenericLoopbackServer CreateServer(GenericLoopbackOptions options = null); - public abstract Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null); + public abstract Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null); public abstract Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null); @@ -28,15 +29,52 @@ public abstract class LoopbackServerFactory // Common helper methods - public Task CreateClientAndServerAsync(Func clientFunc, Func serverFunc, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) + public Task CreateClientAndServerAsync(Func clientFunc, Func serverFunc, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null) { - return CreateServerAsync(async (server, uri) => + times?.Add((100, s.ElapsedMilliseconds)); + var server = CreateServerAsync(async (server, uri) => { - Task clientTask = clientFunc(uri); - Task serverTask = serverFunc(server); + times?.Add((101, s.ElapsedMilliseconds)); + Task clientTask = Task.Run(async () => + { + times?.Add((300, s.ElapsedMilliseconds)); + try + { + times?.Add((301, s.ElapsedMilliseconds)); + await clientFunc(uri); + times?.Add((302, s.ElapsedMilliseconds)); + } + catch + { + times?.Add((303, s.ElapsedMilliseconds)); + throw; + } + times?.Add((304, s.ElapsedMilliseconds)); + }); + times?.Add((102, s.ElapsedMilliseconds)); + Task serverTask = Task.Run(async () => + { + times?.Add((400, s.ElapsedMilliseconds)); + try + { + times?.Add((401, s.ElapsedMilliseconds)); + await serverFunc(server); + times?.Add((402, s.ElapsedMilliseconds)); + } + catch + { + times?.Add((403, s.ElapsedMilliseconds)); + throw; + } + times?.Add((404, s.ElapsedMilliseconds)); + }); + times?.Add((103, s.ElapsedMilliseconds)); await new Task[] { clientTask, serverTask }.WhenAllOrAnyFailed().ConfigureAwait(false); - }, options: options).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); + times?.Add((104, s.ElapsedMilliseconds)); + }, options: options, times: times, s: s).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout + 50000)); + times?.Add((200, s.ElapsedMilliseconds)); + return server; } } diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs index 45df14c0380ea..1ed7f134d4d8b 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net.Security; using System.Net.Sockets; @@ -220,12 +221,30 @@ private static Http2Options CreateOptions(GenericLoopbackOptions options) return http2Options; } - public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) + public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null) { - using (var server = CreateServer(options)) + times?.Add((500, s.ElapsedMilliseconds)); + var server = CreateServer(options); + times?.Add((501, s.ElapsedMilliseconds)); + try { - await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); + times?.Add((502, s.ElapsedMilliseconds)); + await funcAsync(server, server.Address) + .WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout + 10000)); + times?.Add((503, s.ElapsedMilliseconds)); + } + catch + { + times?.Add((504, s.ElapsedMilliseconds)); + throw; + } + finally + { + times?.Add((505, s.ElapsedMilliseconds)); + server.Dispose(); + times?.Add((506, s.ElapsedMilliseconds)); } + times?.Add((507, s.ElapsedMilliseconds)); } public override Version Version => HttpVersion20.Value; diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index 6e511915f047b..f3a8f7b482434 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -94,7 +94,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option return new Http3LoopbackServer(_quicImplementationProvider, CreateOptions(options)); } - public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60000, GenericLoopbackOptions options = null) + public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null) { using GenericLoopbackServer server = CreateServer(options); await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs index 88a8071153e6c..37b9cbf8af234 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net.Security; using System.Net.Sockets; @@ -206,7 +207,7 @@ private static HttpAgnosticOptions CreateOptions(GenericLoopbackOptions options) return httpOptions; } - public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) + public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null) { using (var server = CreateServer(options)) { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index dbb1382c9aaf5..ab0b0a406eda4 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -151,34 +151,72 @@ await LoopbackServerFactory.CreateClientAndServerAsync( [Fact] public async Task GetAsync_AddMultipleCookieHeaders_CookiesSent() { - await LoopbackServerFactory.CreateClientAndServerAsync( - async uri => - { - using (HttpClient client = CreateHttpClient()) + List<(int, long)> times = new List<(int, long)>(); + Stopwatch s = Stopwatch.StartNew(); + + try + { + times.Add((0, s.ElapsedMilliseconds)); + + await LoopbackServerFactory.CreateClientAndServerAsync( + async uri => { - var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; - requestMessage.Headers.Add("Cookie", "A=1"); - requestMessage.Headers.Add("Cookie", "B=2"); - requestMessage.Headers.Add("Cookie", "C=3"); + times.Add((10, s.ElapsedMilliseconds)); + HttpClient client = CreateHttpClient(); + times.Add((11, s.ElapsedMilliseconds)); + try + { + var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; + requestMessage.Headers.Add("Cookie", "A=1"); + requestMessage.Headers.Add("Cookie", "B=2"); + requestMessage.Headers.Add("Cookie", "C=3"); + times.Add((12, s.ElapsedMilliseconds)); - await client.SendAsync(TestAsync, requestMessage); - } - }, - async server => - { - HttpRequestData requestData = await server.HandleRequestAsync(); + await client.SendAsync(TestAsync, requestMessage) + .WaitAsync(TimeSpan.FromSeconds(10)); + + times.Add((13, s.ElapsedMilliseconds)); + } + catch + { + times.Add((14, s.ElapsedMilliseconds)); + throw; + } + finally + { + times.Add((15, s.ElapsedMilliseconds)); + client.Dispose(); + } + }, + async server => + { + times.Add((20, s.ElapsedMilliseconds)); + + HttpRequestData requestData = await server.HandleRequestAsync() + .WaitAsync(TimeSpan.FromSeconds(15)); + + times.Add((21, s.ElapsedMilliseconds)); // Multiple Cookie header values are treated as any other header values and are // concatenated using ", " as the separator. string cookieHeaderValue = requestData.GetSingleHeaderValue("Cookie"); - var cookieValues = cookieHeaderValue.Split(new string[] { ", " }, StringSplitOptions.None); - Assert.Contains("A=1", cookieValues); - Assert.Contains("B=2", cookieValues); - Assert.Contains("C=3", cookieValues); - Assert.Equal(3, cookieValues.Count()); - }); + var cookieValues = cookieHeaderValue.Split(new string[] { ", " }, StringSplitOptions.None); + Assert.Contains("A=1", cookieValues); + Assert.Contains("B=2", cookieValues); + Assert.Contains("C=3", cookieValues); + Assert.Equal(3, cookieValues.Count()); + + times.Add((22, s.ElapsedMilliseconds)); + }, + millisecondsTimeout: 300_000); + } + catch (Exception ex) + { + times.Add((1, s.ElapsedMilliseconds)); + throw new Exception(string.Join('\n', times.Select(t => $"{t.Item1,2} {t.Item2}")), ex); + } } private string GetCookieValue(HttpRequestData request) diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs index 7594b4908010f..ae3a65e02b1a5 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using System.Net.WebSockets; using Xunit; +using System.Diagnostics; namespace System.Net.Test.Common { @@ -1086,7 +1087,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option return loopbackServer; } - public override Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) + public override Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null, List<(int, long)> times = null, Stopwatch s = null) { return LoopbackServer.CreateServerAsync((server, uri) => funcAsync(server, uri), options: CreateOptions(options)); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs index 908efaf6afe32..16c16f482aadd 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs @@ -370,7 +370,7 @@ private void GenLogMethod(LoggerMethod lm, string nestedIndentation) GenDefineTypes(lm, brackets: true); - _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", true); + _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", new global::Microsoft.Extensions.Logging.LogDefineOptions() {{ SkipEnabledCheck = true }}); "); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index ef9d46564ec61..1404880a97288 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -48,6 +48,11 @@ public partial interface ISupportExternalScope { void SetScopeProvider(Microsoft.Extensions.Logging.IExternalScopeProvider scopeProvider); } + public partial class LogDefineOptions + { + public LogDefineOptions() { } + public bool SkipEnabledCheck { get { throw null; } set { } } + } public static partial class LoggerExtensions { public static System.IDisposable BeginScope(this Microsoft.Extensions.Logging.ILogger logger, string messageFormat, params object?[] args) { throw null; } @@ -94,7 +99,7 @@ public static partial class LoggerFactoryExtensions public static partial class LoggerMessage { public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } @@ -103,17 +108,17 @@ public static partial class LoggerMessage public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public sealed partial class LoggerMessageAttribute : System.Attribute diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs new file mode 100644 index 0000000000000..48016fcd2b781 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Extensions.Logging +{ + /// + /// Options for and its overloads + /// + public class LogDefineOptions + { + /// + /// Gets or sets the flag to skip IsEnabled check for the logging method. + /// + public bool SkipEnabledCheck { get; set; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs index ee40fc116bc38..db1ffdfd1d89f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs @@ -128,7 +128,7 @@ public static Func DefineScopeThe named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -136,9 +136,9 @@ public static Func DefineScopeThe /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 0); @@ -147,7 +147,7 @@ void Log(ILogger logger, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -170,7 +170,7 @@ void Log(ILogger logger, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -179,9 +179,9 @@ void Log(ILogger logger, Exception? exception) /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 1); @@ -190,7 +190,7 @@ void Log(ILogger logger, T1 arg1, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -214,7 +214,7 @@ void Log(ILogger logger, T1 arg1, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -224,9 +224,9 @@ void Log(ILogger logger, T1 arg1, Exception? exception) /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 2); @@ -235,7 +235,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -260,7 +260,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -271,9 +271,9 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 3); @@ -282,7 +282,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -308,7 +308,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -320,9 +320,9 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 4); @@ -331,7 +331,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -358,7 +358,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -371,9 +371,9 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 5); @@ -382,7 +382,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Exception? logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -410,7 +410,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Exception? /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -424,9 +424,9 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Exception? /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 6); @@ -435,7 +435,7 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, E logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt index 024e0464c715f..e10a678aef24c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt @@ -17,7 +17,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses.NestedNamesp { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action __M9Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), "M9", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), "M9", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M9(global::Microsoft.Extensions.Logging.ILogger logger) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt index c4b242a0a9e17..6576bb4b394f1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt index ae5b89b74a4d4..5daee3ddd43a1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action, global::System.Exception?> __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {a1} {a2}", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {a1} {a2}", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 a1, global::System.Collections.Generic.IEnumerable a2) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs index 6422a7a188b86..a47ccc154fc6c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs @@ -430,13 +430,13 @@ public void DefineAndDefineScope_ThrowsException_WhenFormatString_IsNull(Delegat public static IEnumerable LogMessagesDataSkipEnabledCheck => new[] { - new object[] { LoggerMessage.Define(LogLevel.Error, 0, "Log ", skipEnabledCheck: true), 0 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 1, "Log {P0}", skipEnabledCheck: true), 1 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 2, "Log {P0} {P1}", skipEnabledCheck: true), 2 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 3, "Log {P0} {P1} {P2}", skipEnabledCheck: true), 3 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 4, "Log {P0} {P1} {P2} {P3}", skipEnabledCheck: true), 4 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 5, "Log {P0} {P1} {P2} {P3} {P4}", skipEnabledCheck: true), 5 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 6, "Log {P0} {P1} {P2} {P3} {P4} {P5}", skipEnabledCheck: true), 6 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 0, "Log ", options: new LogDefineOptions() { SkipEnabledCheck = true }), 0 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 1, "Log {P0}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 1 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 2, "Log {P0} {P1}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 2 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 3, "Log {P0} {P1} {P2}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 3 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 4, "Log {P0} {P1} {P2} {P3}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 4 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 5, "Log {P0} {P1} {P2} {P3} {P4}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 5 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 6, "Log {P0} {P1} {P2} {P3} {P4} {P5}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 6 }, }; private delegate Delegate Define(LogLevel logLevel, EventId eventId, string formatString); diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c index 7c02c4ede71a4..4a8d5bcd72153 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c @@ -112,6 +112,17 @@ static const UChar g_HalfFullHigherChars[] = { }; static const int32_t g_HalfFullCharsLength = (sizeof(g_HalfFullHigherChars) / sizeof(UChar)); +// Hiragana without [semi-]voiced sound mark for custom collation rules +// If Hiragana with [semi-]voiced sound mark is added to custom collation rules, there is a conflict +// between the custom rule and some default rule. +static const UChar g_HiraganaWithoutVoicedSoundMarkChars[] = { + 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, + 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, 0x3061, 0x3063, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, + 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, + 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093, 0x3095, 0x3096, 0x309D, +}; +static const int32_t g_HiraganaWithoutVoicedSoundMarkCharsLength = (sizeof(g_HiraganaWithoutVoicedSoundMarkChars) / sizeof(UChar)); + /* ICU collation rules reserve any punctuation and whitespace characters for use in the syntax. Thus, to use these characters in a rule, they need to be escaped. @@ -150,22 +161,36 @@ custom rules in order to support IgnoreKanaType and IgnoreWidth CompareOptions c */ static void FillIgnoreKanaRules(UChar* completeRules, int32_t* fillIndex, int32_t completeRulesLength, int32_t isIgnoreKanaType) { - UChar compareChar = isIgnoreKanaType ? '=' : '<'; - assert((*fillIndex) + (4 * (hiraganaEnd - hiraganaStart + 1)) <= completeRulesLength); if ((*fillIndex) + (4 * (hiraganaEnd - hiraganaStart + 1)) > completeRulesLength) // check the allocated the size { return; } - for (UChar hiraganaChar = hiraganaStart; hiraganaChar <= hiraganaEnd; hiraganaChar++) + if (isIgnoreKanaType) + { + for (UChar hiraganaChar = hiraganaStart; hiraganaChar <= hiraganaEnd; hiraganaChar++) + { + // Hiragana is the range 3041 to 3096 & 309D & 309E + if (hiraganaChar <= 0x3096 || hiraganaChar >= 0x309D) // characters between 3096 and 309D are not mapped to katakana + { + completeRules[*fillIndex] = '&'; + completeRules[(*fillIndex) + 1] = hiraganaChar; + completeRules[(*fillIndex) + 2] = '='; + completeRules[(*fillIndex) + 3] = hiraganaChar + hiraganaToKatakanaOffset; + (*fillIndex) += 4; + } + } + } + else { - // Hiragana is the range 3041 to 3096 & 309D & 309E - if (hiraganaChar <= 0x3096 || hiraganaChar >= 0x309D) // characters between 3096 and 309D are not mapped to katakana + // Avoid conflicts between default [semi-]voiced sound mark rules and custom rules + for (int i = 0; i < g_HiraganaWithoutVoicedSoundMarkCharsLength; i++) { + UChar hiraganaChar = g_HiraganaWithoutVoicedSoundMarkChars[i]; completeRules[*fillIndex] = '&'; completeRules[(*fillIndex) + 1] = hiraganaChar; - completeRules[(*fillIndex) + 2] = compareChar; + completeRules[(*fillIndex) + 2] = '<'; completeRules[(*fillIndex) + 3] = hiraganaChar + hiraganaToKatakanaOffset; (*fillIndex) += 4; } diff --git a/src/libraries/System.Composition.AttributedModel/Directory.Build.props b/src/libraries/System.Composition.AttributedModel/Directory.Build.props deleted file mode 100644 index 67ee2eb47605a..0000000000000 --- a/src/libraries/System.Composition.AttributedModel/Directory.Build.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - Provides common attributes used by System.Composition types. - -Commonly Used Types: -System.Composition.ExportAttribute -System.Composition.ImportAttribute -System.Composition.Convention.AttributedModelProvider - - \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj b/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj deleted file mode 100644 index 4c2c1ff8a36a9..0000000000000 --- a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..e80b49dae2deb --- /dev/null +++ b/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj index 7d49a3802f966..70f72d925e7fb 100644 --- a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj +++ b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj @@ -1,6 +1,14 @@ $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + Provides common attributes used by System.Composition types. + +Commonly Used Types: +System.Composition.ExportAttribute +System.Composition.ImportAttribute +System.Composition.Convention.AttributedModelProvider @@ -22,4 +30,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/Directory.Build.props b/src/libraries/System.Composition.Convention/Directory.Build.props deleted file mode 100644 index 8ea89c583495b..0000000000000 --- a/src/libraries/System.Composition.Convention/Directory.Build.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - Provides types that support using Managed Extensibility Framework with a convention-based configuration model. - -Commonly Used Types: -System.Composition.Convention.ConventionBuilder -System.Composition.Convention.ExportConventionBuilder -System.Composition.Convention.ImportConventionBuilder -System.Composition.Convention.PartConventionBuilder -System.Composition.Convention.ParameterImportConventionBuilder - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj b/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj deleted file mode 100644 index 957c0d82866a0..0000000000000 --- a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..e80b49dae2deb --- /dev/null +++ b/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj index 500ab5bb2a271..eb348cc059b35 100644 --- a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj +++ b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj @@ -2,6 +2,16 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides types that support using Managed Extensibility Framework with a convention-based configuration model. + +Commonly Used Types: +System.Composition.Convention.ConventionBuilder +System.Composition.Convention.ExportConventionBuilder +System.Composition.Convention.ImportConventionBuilder +System.Composition.Convention.PartConventionBuilder +System.Composition.Convention.ParameterImportConventionBuilder @@ -34,4 +44,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/Directory.Build.props b/src/libraries/System.Composition.Hosting/Directory.Build.props deleted file mode 100644 index 80edbe7be3b38..0000000000000 --- a/src/libraries/System.Composition.Hosting/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - Provides Managed Extensibility Framework types that are useful to developers of extensible applications, or hosts. - -Commonly Used Types: -System.Composition.Hosting.CompositionHost - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj b/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj deleted file mode 100644 index d319cbd0c8e27..0000000000000 --- a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..e80b49dae2deb --- /dev/null +++ b/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj index 437ad767826ff..b15d5cb0895f0 100644 --- a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj +++ b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj @@ -2,6 +2,12 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides Managed Extensibility Framework types that are useful to developers of extensible applications, or hosts. + +Commonly Used Types: +System.Composition.Hosting.CompositionHost @@ -49,4 +55,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/Directory.Build.props b/src/libraries/System.Composition.Runtime/Directory.Build.props deleted file mode 100644 index 8464a7c13943e..0000000000000 --- a/src/libraries/System.Composition.Runtime/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - Contains runtime components of the Managed Extensibility Framework. - -Commonly Used Types: -System.Composition.CompositionContext - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj b/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj deleted file mode 100644 index acdef61a398d0..0000000000000 --- a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..e80b49dae2deb --- /dev/null +++ b/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj index b5ecf044a581a..2dcd969991e4a 100644 --- a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj +++ b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj @@ -2,6 +2,12 @@ System.Composition $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + Contains runtime components of the Managed Extensibility Framework. + +Commonly Used Types: +System.Composition.CompositionContext @@ -19,4 +25,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/Directory.Build.props b/src/libraries/System.Composition.TypedParts/Directory.Build.props deleted file mode 100644 index 4169d3592bfdf..0000000000000 --- a/src/libraries/System.Composition.TypedParts/Directory.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - Provides some extension methods for the Managed Extensibility Framework. - -Commonly Used Types: -System.Composition.CompositionContextExtensions -System.Composition.Hosting.ContainerConfiguration - - \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj b/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj deleted file mode 100644 index e7ea55ed613c0..0000000000000 --- a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..e80b49dae2deb --- /dev/null +++ b/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj index c8828d012e679..3843a7ecc5932 100644 --- a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj +++ b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj @@ -3,6 +3,13 @@ System.Composition $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides some extension methods for the Managed Extensibility Framework. + +Commonly Used Types: +System.Composition.CompositionContextExtensions +System.Composition.Hosting.ContainerConfiguration @@ -43,4 +50,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition/Directory.Build.props b/src/libraries/System.Composition/Directory.Build.props deleted file mode 100644 index be90cca2f6ea3..0000000000000 --- a/src/libraries/System.Composition/Directory.Build.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - Microsoft - This packages provides a version of the Managed Extensibility Framework (MEF) that is lightweight and specifically optimized for high throughput scenarios, such as the web. - -Commonly Used Types: -System.Composition.ExportAttribute -System.Composition.ImportAttribute -System.Composition.Convention.ConventionBuilder -System.Composition.Hosting.CompositionHost -System.Composition.CompositionContext -System.Composition.CompositionContextExtensions - - diff --git a/src/libraries/System.Composition/pkg/System.Composition.pkgproj b/src/libraries/System.Composition/pkg/System.Composition.pkgproj deleted file mode 100644 index 3998ca2d92211..0000000000000 --- a/src/libraries/System.Composition/pkg/System.Composition.pkgproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition/src/System.Composition.csproj b/src/libraries/System.Composition/src/System.Composition.csproj new file mode 100644 index 0000000000000..9314413cfd507 --- /dev/null +++ b/src/libraries/System.Composition/src/System.Composition.csproj @@ -0,0 +1,28 @@ + + + $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + + $(NoWarn);NU5128 + + false + This packages provides a version of the Managed Extensibility Framework (MEF) that is lightweight and specifically optimized for high throughput scenarios, such as the web. + +Commonly Used Types: +System.Composition.ExportAttribute +System.Composition.ImportAttribute +System.Composition.Convention.ConventionBuilder +System.Composition.Hosting.CompositionHost +System.Composition.CompositionContext +System.Composition.CompositionContextExtensions + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition/src/System.Composition.proj b/src/libraries/System.Composition/src/System.Composition.proj deleted file mode 100644 index 8d90792dd6ffe..0000000000000 --- a/src/libraries/System.Composition/src/System.Composition.proj +++ /dev/null @@ -1,6 +0,0 @@ - - - - netstandard2.0 - - \ No newline at end of file diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs index be84dbaf83661..eb545649d3667 100644 --- a/src/libraries/System.Console/src/System/TermInfo.cs +++ b/src/libraries/System.Console/src/System/TermInfo.cs @@ -178,7 +178,8 @@ private Database(string term, byte[] data) "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo", - "/usr/share/misc/terminfo" + "/usr/share/misc/terminfo", + "/usr/local/share/terminfo" }; /// Read the database for the specified terminal. diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props b/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props index a3b1dbe534868..e7e8cb9ac081b 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props +++ b/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props @@ -3,9 +3,5 @@ Open windows - Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. - -Commonly Used Types: -System.Diagnostics.PerformanceCounter \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj b/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj deleted file mode 100644 index 2fef4179be2b7..0000000000000 --- a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - - - - - - - diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml new file mode 100644 index 0000000000000..fe46e5dfbd636 --- /dev/null +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml @@ -0,0 +1,124 @@ + + + + + CP0001 + T:System.Diagnostics.CounterCreationData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterCreationDataCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterSample + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterSampleCalculator + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.ICollectData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceDataCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceDataCollectionCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounter + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterCategory + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterCategoryType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterInstanceLifetime + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterManager + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSet + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstance + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstanceCounterDataSet + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstanceType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index 600f6fc58b993..51468f0d57d0f 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -3,6 +3,11 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 $(NoWarn);CA1847 + true + Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. + +Commonly Used Types: +System.Diagnostics.PerformanceCounter @@ -149,9 +154,7 @@ - - - - + + diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index a0b7401fb0ac2..0000000000000 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - ILLink - IL2050 - member - M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) - - - diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index b02d55a46c6a2..ff707877c2d11 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -192,6 +192,7 @@ + @@ -294,8 +295,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.GlobalLock.cs" /> - + + - + + + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs index 4fe1b54ae1b83..84f959f94c281 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs @@ -9,25 +9,24 @@ namespace System.Drawing { public sealed partial class Bitmap { - public Bitmap(Stream stream, bool useIcm) + public unsafe Bitmap(Stream stream, bool useIcm) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - IntPtr bitmap = IntPtr.Zero; - int status; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr bitmap = IntPtr.Zero; if (useIcm) { - status = Gdip.GdipCreateBitmapFromStreamICM(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStreamICM(streamWrapper.Ptr, &bitmap)); } else { - status = Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStream(streamWrapper.Ptr, &bitmap)); } - Gdip.CheckStatus(status); ValidateImage(bitmap); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs similarity index 89% rename from src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs rename to src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs index 236ae1be2aa03..5889a4ac704b4 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs @@ -14,16 +14,16 @@ namespace System.Drawing /// /// Supports IStream and IPicture COM interfaces. /// - internal unsafe class DrawingComWrappers : ComWrappers + internal unsafe partial class DrawingCom : ComWrappers { private const int S_OK = (int)Interop.HRESULT.S_OK; private static readonly Guid IID_IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry(); - internal static DrawingComWrappers Instance { get; } = new DrawingComWrappers(); + internal static DrawingCom Instance { get; } = new DrawingCom(); - private DrawingComWrappers() { } + private DrawingCom() { } private static ComInterfaceEntry* InitializeComInterfaceEntry() { @@ -31,7 +31,7 @@ private DrawingComWrappers() { } IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInterface, fpAddRef, fpRelease); - ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingCom), sizeof(ComInterfaceEntry)); wrapperEntry->IID = IID_IStream; wrapperEntry->Vtable = iStreamVtbl; return wrapperEntry; @@ -66,6 +66,27 @@ protected override void ReleaseObjects(IEnumerable objects) throw new NotImplementedException(); } + internal static IStreamWrapper GetComWrapper(Interop.Ole32.IStream stream) + { + IntPtr streamWrapperPtr = Instance.GetOrCreateComInterfaceForObject(stream, CreateComInterfaceFlags.None); + + Guid streamIID = IID_IStream; + int result = Marshal.QueryInterface(streamWrapperPtr, ref streamIID, out IntPtr streamPtr); + + Marshal.Release(streamWrapperPtr); + + ThrowExceptionForHR(result); + + return new IStreamWrapper(streamPtr); + } + + internal static void ThrowExceptionForHR(int errorCode) + { + // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only + // throw the Exception corresponding to the specified errorCode. + Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); + } + internal static class IStreamVtbl { public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease) @@ -159,16 +180,13 @@ private static int CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - Interop.Ole32.IStream pstmStream = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)pstm); - inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten); + return (int)inst.CopyTo(pstm, cb, pcbRead, pcbWritten); } catch (Exception e) { return e.HResult; } - - return S_OK; } [UnmanagedCallersOnly] @@ -250,23 +268,16 @@ private static int Stat(IntPtr thisPtr, Interop.Ole32.STATSTG* pstatstg, Interop [UnmanagedCallersOnly] private static int Clone(IntPtr thisPtr, IntPtr* ppstm) { - if (ppstm == null) - { - return (int)Interop.HRESULT.STG_E_INVALIDPOINTER; - } - try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - *ppstm = Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None); + return (int)inst.Clone(ppstm); } catch (Exception e) { return e.HResult; } - - return S_OK; } } @@ -297,7 +308,7 @@ public unsafe int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize) { // Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation Guid streamIID = IID_IStream; - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); + ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); try { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs new file mode 100644 index 0000000000000..d3fdd2115bffb --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Internal; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal static partial class DrawingCom + { + internal static IStreamWrapper GetComWrapper(GPStream stream) + { + return new IStreamWrapper(Marshal.GetComInterfaceForObject(stream)); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs new file mode 100644 index 0000000000000..a5b53e65ff589 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal partial class DrawingCom + { + internal readonly struct IStreamWrapper : IDisposable + { + public readonly IntPtr Ptr; + + public IStreamWrapper(IntPtr ptr) + { + Ptr = ptr; + } + + public void Dispose() + { + Marshal.Release(Ptr); + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index 47aeb4f9397c3..3edf4a0e7b67e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -210,10 +210,10 @@ private static void PlatformInitialize() internal static extern int GdipDeleteBrush(HandleRef brush); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStream(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStreamICM(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipCloneImage(HandleRef image, out IntPtr cloneimage); @@ -222,7 +222,7 @@ private static void PlatformInitialize() internal static extern int GdipSaveImageToFile(HandleRef image, string filename, ref Guid classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSaveImageToStream(HandleRef image, Interop.Ole32.IStream stream, ref Guid classId, HandleRef encoderParams); + internal static extern int GdipSaveImageToStream(HandleRef image, IntPtr stream, Guid* classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipSaveAdd(HandleRef image, HandleRef encoderParams); @@ -327,7 +327,7 @@ private static void PlatformInitialize() internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream stream, IntPtr header); + internal static extern int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetMetafileHeaderFromMetafile(HandleRef metafile, IntPtr header); @@ -336,16 +336,16 @@ private static void PlatformInitialize() internal static extern int GdipGetHemfFromMetafile(HandleRef metafile, out IntPtr hEnhMetafile); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromStream(Interop.Ole32.IStream stream, out IntPtr metafile); + internal static extern int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStreamI(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipComment(HandleRef graphics, int sizeData, byte[] data); @@ -354,10 +354,10 @@ private static void PlatformInitialize() internal static extern int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStream(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs index a03ad5ed133bb..3d9ad1a356676 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -28,7 +28,7 @@ public unsafe void Save(Stream outputStream) // But, in the interest of simplicity, we just call to // OLE to do it for us. PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); - Guid iid = DrawingComWrappers.IPicture.IID; + Guid iid = DrawingCom.IPicture.IID; IntPtr lpPicture; Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); @@ -36,13 +36,13 @@ public unsafe void Save(Stream outputStream) try { // Use UniqueInstance here because we never want to cache the wrapper. It only gets used once and then disposed. - using DrawingComWrappers.IPicture picture = (DrawingComWrappers.IPicture)DrawingComWrappers.Instance + using DrawingCom.IPicture picture = (DrawingCom.IPicture)DrawingCom.Instance .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); var gpStream = new GPStream(outputStream, makeSeekable: false); - streamPtr = DrawingComWrappers.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + streamPtr = DrawingCom.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); - CheckSaveAsFileResult(picture.SaveAsFile(streamPtr, -1, null)); + DrawingCom.ThrowExceptionForHR(picture.SaveAsFile(streamPtr, -1, null)); } finally { @@ -61,13 +61,6 @@ public unsafe void Save(Stream outputStream) } } - private static void CheckSaveAsFileResult(int errorCode) - { - // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only - // throw the Exception corresponding to the specified errorCode. - Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); - } - [DllImport(Interop.Libraries.Oleaut32)] private static unsafe extern int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index a6dd53744a974..ccea5ff42d52e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -23,16 +23,7 @@ public static Image FromStream(Stream stream, bool useEmbeddedColorManagement, b if (stream == null) throw new ArgumentNullException(nameof(stream)); - IntPtr image = IntPtr.Zero; - - if (useEmbeddedColorManagement) - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(new GPStream(stream), out image)); - } - else - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); - } + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement); if (validateImageData) ValidateImage(image); @@ -45,9 +36,7 @@ public static Image FromStream(Stream stream, bool useEmbeddedColorManagement, b // Used for serialization private IntPtr InitializeFromStream(Stream stream) { - IntPtr image = IntPtr.Zero; - - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement: false); ValidateImage(image); nativeImage = image; @@ -59,6 +48,22 @@ private IntPtr InitializeFromStream(Stream stream) return image; } + private static unsafe IntPtr LoadGdipImageFromStream(GPStream stream, bool useEmbeddedColorManagement) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(stream); + + IntPtr image = IntPtr.Zero; + if (useEmbeddedColorManagement) + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(streamWrapper.Ptr, &image)); + } + else + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(streamWrapper.Ptr, &image)); + } + return image; + } + internal Image(IntPtr nativeImage) => SetNativeImage(nativeImage); /// @@ -240,11 +245,15 @@ public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters? encod if (!saved) { - Gdip.CheckStatus(Gdip.GdipSaveImageToStream( - new HandleRef(this, nativeImage), - new GPStream(stream, makeSeekable: false), - ref g, - new HandleRef(encoderParams, encoderParamsMemory))); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream, makeSeekable: false)); + unsafe + { + Gdip.CheckStatus(Gdip.GdipSaveImageToStream( + new HandleRef(this, nativeImage), + streamWrapper.Ptr, + &g, + new HandleRef(encoderParams, encoderParamsMemory))); + } } } finally diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs index 447799333febf..4d8dccb01e71f 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs @@ -1,11 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.InteropServices; -using System.IO; using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; using Gdip = System.Drawing.SafeNativeMethods.Gdip; -using System.Runtime.Serialization; namespace System.Drawing.Imaging { @@ -27,14 +26,18 @@ public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader) : /// /// Initializes a new instance of the class from the specified stream. /// - public Metafile(Stream stream) + public unsafe Metafile(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(new GPStream(stream), out IntPtr metafile)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(streamWrapper.Ptr, &metafile)); + SetNativeImage(metafile); } @@ -145,16 +148,19 @@ public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, Metaf /// /// Initializes a new instance of the class from the specified data stream. /// - public Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, MetafileFrameUnit.GdiCompatible, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -162,16 +168,19 @@ public Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? descri /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -179,31 +188,32 @@ public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, Metafi /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { - IntPtr metafile = IntPtr.Zero; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr metafile = IntPtr.Zero; if (frameRect.IsEmpty) { Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, frameUnit, description, - out metafile)); + &metafile)); } else { Gdip.CheckStatus(Gdip.GdipRecordMetafileStreamI( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out metafile)); + &metafile)); } SetNativeImage(metafile); @@ -292,7 +302,8 @@ public static MetafileHeader GetMetafileHeader(Stream stream) try { - Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(new GPStream(stream), memory)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(streamWrapper.Ptr, memory)); int[] type = new int[] { 0 }; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs new file mode 100644 index 0000000000000..65079d5698a4b --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public unsafe Interop.HRESULT Clone(IntPtr* ppstm) + { + if (ppstm == null) + { + return Interop.HRESULT.STG_E_INVALIDPOINTER; + } + + // The cloned object should have the same current "position" + var clone = new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + + *ppstm = DrawingCom.Instance.GetOrCreateComInterfaceForObject(clone, CreateComInterfaceFlags.None); + + return Interop.HRESULT.S_OK; + } + + public unsafe Interop.HRESULT CopyTo(IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + Interop.HRESULT hr = (Interop.HRESULT)WriteToStream(pstm, b, read, &written); + if (hr != Interop.HRESULT.S_OK) + { + return hr; + } + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + { + *pcbRead = totalRead; + } + + if (pcbWritten != null) + { + *pcbWritten = totalWritten; + } + + return Interop.HRESULT.S_OK; + } + + private static unsafe int WriteToStream(IntPtr pstm, byte* pv, uint cb, uint* pcbWritten) + { + return ((delegate* unmanaged)(*(*(void***)pstm + 4 /* IStream.Write slot */))) + (pstm, pv, cb, pcbWritten); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs new file mode 100644 index 0000000000000..0c4e56ef8d685 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public Interop.Ole32.IStream Clone() + { + // The cloned object should have the same current "position" + return new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + } + + public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + pstm.Write(b, read, &written); + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + *pcbRead = totalRead; + + if (pcbWritten != null) + *pcbWritten = totalWritten; + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs index dd6a8a16350ff..524be173b52cf 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs @@ -1,12 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.IO; namespace System.Drawing.Internal { - internal sealed class GPStream : Interop.Ole32.IStream + internal sealed partial class GPStream : Interop.Ole32.IStream { private readonly Stream _dataStream; @@ -41,15 +40,6 @@ private void ActualizeVirtualPosition() _virtualPosition = -1; } - public Interop.Ole32.IStream Clone() - { - // The cloned object should have the same current "position" - return new GPStream(_dataStream) - { - _virtualPosition = _virtualPosition - }; - } - public void Commit(uint grfCommitFlags) { _dataStream.Flush(); @@ -58,43 +48,6 @@ public void Commit(uint grfCommitFlags) ActualizeVirtualPosition(); } - public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) - { - byte[] buffer = ArrayPool.Shared.Rent(4096); - - ulong remaining = cb; - ulong totalWritten = 0; - ulong totalRead = 0; - - fixed (byte* b = buffer) - { - while (remaining > 0) - { - uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; - Read(b, read, &read); - remaining -= read; - totalRead += read; - - if (read == 0) - { - break; - } - - uint written; - pstm.Write(b, read, &written); - totalWritten += written; - } - } - - ArrayPool.Shared.Return(buffer); - - if (pcbRead != null) - *pcbRead = totalRead; - - if (pcbWritten != null) - *pcbWritten = totalWritten; - } - public unsafe void Read(byte* pv, uint cb, uint* pcbRead) { ActualizeVirtualPosition(); diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs index 26207c1ccf733..ca3800ccfa50e 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs @@ -240,6 +240,20 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreCase, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "c", "C", CompareOptions.IgnoreKanaType, -1 }; + // Japanese [semi-]voiced sound mark + yield return new object[] { s_invariantCompare, "\u306F", "\u3070", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3071", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u3070", "\u3071", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D0", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D1", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30D0", "\u30D1", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3070", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3071", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u3070", "\u3071", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D0", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D1", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30D0", "\u30D1", CompareOptions.IgnoreNonSpace, 0 }; + // Spanish yield return new object[] { new CultureInfo("es-ES").CompareInfo, "llegar", "lugar", CompareOptions.None, -1 }; @@ -485,5 +499,81 @@ public void TestIgnoreKanaAndWidthCases() $"Expect '{(int)hiraganaChar:x4}' == {(int)hiraganaChar + hiraganaToKatakanaOffset:x4} with CompareOptions.IgnoreKanaType"); } } + + [Fact] + public void TestHiraganaAndKatakana() + { + const char hiraganaStart = '\u3041'; + const char hiraganaEnd = '\u3096'; + const int hiraganaToKatakanaOffset = 0x30a1 - 0x3041; + List hiraganaList = new List(); + for (char c = hiraganaStart; c <= hiraganaEnd; c++) // Hiragana + { + // TODO: small hiragana/katakana orders + // https://github.com/dotnet/runtime/issues/54987 + switch (c) + { + case '\u3041': + case '\u3043': + case '\u3045': + case '\u3047': + case '\u3049': + case '\u3063': + case '\u3083': + case '\u3085': + case '\u3087': + case '\u308E': + case '\u3095': + case '\u3096': + break; + default: + hiraganaList.Add(c); + break; + } + } + for (char c = '\u309D'; c <= '\u309E'; c++) // Hiragana iteration mark + { + hiraganaList.Add(c); + } + CompareOptions[] options = new[] { + CompareOptions.None, + CompareOptions.IgnoreCase, + CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreSymbols, + CompareOptions.IgnoreKanaType, + CompareOptions.IgnoreWidth, + CompareOptions.Ordinal, + CompareOptions.OrdinalIgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + }; + + foreach (var option in options) + { + for (int i = 0; i < hiraganaList.Count; i++) + { + char hiraganaChar1 = hiraganaList[i]; + char katakanaChar1 = (char)(hiraganaChar1 + hiraganaToKatakanaOffset); + + for (int j = i; j < hiraganaList.Count; j++) + { + char hiraganaChar2 = hiraganaList[j]; + char katakanaChar2 = (char)(hiraganaChar2 + hiraganaToKatakanaOffset); + + int hiraganaResult = s_invariantCompare.Compare(new string(hiraganaChar1, 1), new string(hiraganaChar2, 1), option); + int katakanaResult = s_invariantCompare.Compare(new string(katakanaChar1, 1), new string(katakanaChar2, 1), option); + Assert.True(hiraganaResult == katakanaResult, + $"Expect Compare({(int)hiraganaChar1:x4}, {(int)hiraganaChar2:x4}) == Compare({(int)katakanaChar1:x4}, {(int)katakanaChar2:x4}) with CompareOptions.{option}"); + } + } + } + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/File/OpenHandle.cs b/src/libraries/System.IO.FileSystem/tests/File/OpenHandle.cs index a283b01a0b7e9..a8b3619fb51cf 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/OpenHandle.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/OpenHandle.cs @@ -56,15 +56,18 @@ public void SafeFileHandle_IsAsync_ReturnsCorrectInformation(FileOptions options } } - // Unix doesn't directly support DeleteOnClose - // For FileStream created out of path, we mimic it by closing the handle first - // and then unlinking the path - // Since SafeFileHandle does not always have the path and we can't find path for given file descriptor on Unix - // this test runs only on Windows - [PlatformSpecific(TestPlatforms.Windows)] [Theory] [InlineData(FileOptions.DeleteOnClose)] [InlineData(FileOptions.DeleteOnClose | FileOptions.Asynchronous)] - public override void DeleteOnClose_FileDeletedAfterClose(FileOptions options) => base.DeleteOnClose_FileDeletedAfterClose(options); + public void DeleteOnClose_FileDeletedAfterSafeHandleDispose(FileOptions options) + { + string path = GetTestFilePath(); + Assert.False(File.Exists(path)); + using (SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, options)) + { + Assert.True(File.Exists(path)); + } + Assert.False(File.Exists(path)); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Name.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Name.cs index cfe0c62d6c30f..13cacf29bf652 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Name.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Name.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Tests; +using Microsoft.Win32.SafeHandles; using Xunit; namespace System.IO.Tests @@ -35,14 +36,29 @@ public void NameNormalizesPath() } [Fact] - public void NameReturnsUnknownForHandle() + public void ConstructFileStreamFromHandle_NameMatchesOriginal() { - using (new ThreadCultureChange(CultureInfo.InvariantCulture)) - using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite)) - using (FileStream fsh = new FileStream(fs.SafeFileHandle, FileAccess.ReadWrite)) - { - Assert.Equal("[Unknown]", fsh.Name); - } + string path = GetTestFilePath(); + using var _ = new ThreadCultureChange(CultureInfo.InvariantCulture); + + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite); + Assert.Equal(path, fs.Name); + + using FileStream fsh = new FileStream(fs.SafeFileHandle, FileAccess.ReadWrite); + Assert.Equal(path, fsh.Name); + } + + [Fact] + public void ConstructFileStreamFromHandleClone_NameReturnsUnknown() + { + string path = GetTestFilePath(); + using var _ = new ThreadCultureChange(CultureInfo.InvariantCulture); + + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite); + Assert.Equal(path, fs.Name); + + using FileStream fsh = new FileStream(new SafeFileHandle(fs.SafeFileHandle.DangerousGetHandle(), ownsHandle: false), FileAccess.ReadWrite); + Assert.Equal("[Unknown]", fsh.Name); } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs index 61efd825f6e48..c35e5f3787403 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Win32.SafeHandles; using Xunit; namespace System.IO.Tests @@ -79,7 +80,7 @@ public void ValidFileOptions_Encrypted(FileOptions option) [Theory] [InlineData(FileOptions.DeleteOnClose)] [InlineData(FileOptions.DeleteOnClose | FileOptions.Asynchronous)] - public virtual void DeleteOnClose_FileDeletedAfterClose(FileOptions options) + public void DeleteOnClose_FileDeletedAfterClose(FileOptions options) { string path = GetTestFilePath(); Assert.False(File.Exists(path)); @@ -89,5 +90,37 @@ public virtual void DeleteOnClose_FileDeletedAfterClose(FileOptions options) } Assert.False(File.Exists(path)); } + + [Theory] + [InlineData(FileOptions.DeleteOnClose)] + [InlineData(FileOptions.DeleteOnClose | FileOptions.Asynchronous)] + public void DeleteOnClose_FileDeletedAfterSafeHandleRelease(FileOptions options) + { + string path = GetTestFilePath(); + Assert.False(File.Exists(path)); + + using (FileStream fs = CreateFileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 0x1000, options)) + { + Assert.True(File.Exists(path)); + + bool added = false; + try + { + fs.SafeFileHandle.DangerousAddRef(ref added); + + fs.Dispose(); + Assert.True(File.Exists(path)); + } + finally + { + if (added) + { + fs.SafeFileHandle.DangerousRelease(); + } + + Assert.False(File.Exists(path)); + } + } + } } } diff --git a/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs b/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs index cf2242b414126..eb7d20b6ac771 100644 --- a/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs @@ -1650,7 +1650,6 @@ public static void ConvertDoubleToNullableShortTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/13651")] public static void ConvertDoubleToUIntTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1660,7 +1659,6 @@ public static void ConvertDoubleToUIntTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/13651")] public static void ConvertDoubleToNullableUIntTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1679,7 +1677,6 @@ public static void ConvertDoubleToULongTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertDoubleToNullableULongTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1905,7 +1902,6 @@ public static void ConvertNullableDoubleToNullableShortTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToUIntTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1915,7 +1911,6 @@ public static void ConvertNullableDoubleToUIntTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToNullableUIntTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1934,7 +1929,6 @@ public static void ConvertNullableDoubleToULongTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToNullableULongTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -3096,7 +3090,6 @@ public static void ConvertFloatToNullableShortTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/47374", TestRuntimes.CoreCLR)] public static void ConvertFloatToUIntTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3106,7 +3099,6 @@ public static void ConvertFloatToUIntTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/47374", TestRuntimes.CoreCLR)] public static void ConvertFloatToNullableUIntTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3124,9 +3116,7 @@ public static void ConvertFloatToULongTest(bool useInterpreter) } } - [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertFloatToNullableULongTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3352,7 +3342,6 @@ public static void ConvertNullableFloatToNullableShortTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToUIntTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3362,7 +3351,6 @@ public static void ConvertNullableFloatToUIntTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToNullableUIntTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3381,7 +3369,6 @@ public static void ConvertNullableFloatToULongTest(bool useInterpreter) } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToNullableULongTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs index 302a4da145f0f..165004afbf2d7 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs @@ -9,14 +9,16 @@ namespace System public partial class BinaryData { public BinaryData(byte[] data) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public BinaryData(object? jsonSerializable, System.Text.Json.JsonSerializerOptions? options = null, System.Type? type = null) { } public BinaryData(System.ReadOnlyMemory data) { } public BinaryData(string data) { } - public static BinaryData Empty { get; } + public static System.BinaryData Empty { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public static System.BinaryData FromBytes(byte[] data) { throw null; } public static System.BinaryData FromBytes(System.ReadOnlyMemory data) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static System.BinaryData FromObjectAsJson(T jsonSerializable, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static System.BinaryData FromStream(System.IO.Stream stream) { throw null; } public static System.Threading.Tasks.Task FromStreamAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -27,6 +29,7 @@ public BinaryData(string data) { } public static implicit operator System.ReadOnlySpan (System.BinaryData? data) { throw null; } public byte[] ToArray() { throw null; } public System.ReadOnlyMemory ToMemory() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public T? ToObjectFromJson(System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public System.IO.Stream ToStream() { throw null; } public override string ToString() { throw null; } diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj index 30f156a04fc00..01e210e939f3e 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj @@ -6,6 +6,9 @@ + + + diff --git a/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index f3e2744fd5852..0000000000000 --- a/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.BinaryData.#ctor(System.Object,System.Text.Json.JsonSerializerOptions,System.Type) - - - ILLink - IL2026 - member - M:System.BinaryData.FromObjectAsJson``1(``0,System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2026 - member - M:System.BinaryData.ToObjectFromJson``1(System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2067 - member - M:System.BinaryData.#ctor(System.Object,System.Text.Json.JsonSerializerOptions,System.Type) - - - ILLink - IL2087 - member - M:System.BinaryData.FromObjectAsJson``1(``0,System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2087 - member - M:System.BinaryData.ToObjectFromJson``1(System.Text.Json.JsonSerializerOptions) - - - \ No newline at end of file diff --git a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj index b7b49395879d2..1505c60f012be 100644 --- a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj @@ -13,6 +13,10 @@ Link="Common\System\Threading\Tasks\TaskToApm.cs" /> + + + + diff --git a/src/libraries/System.Memory.Data/src/System/BinaryData.cs b/src/libraries/System.Memory.Data/src/System/BinaryData.cs index b0e56d1e7bae3..2e5dfbfacfb68 100644 --- a/src/libraries/System.Memory.Data/src/System/BinaryData.cs +++ b/src/libraries/System.Memory.Data/src/System/BinaryData.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; @@ -17,6 +16,8 @@ namespace System /// public class BinaryData { + private const string JsonSerializerRequiresUnreferencedCode = "JSON serialization and deserialization might require types that cannot be statically analyzed."; + /// /// The backing store for the instance. /// @@ -41,12 +42,12 @@ public BinaryData(byte[] data) /// Creates a instance by serializing the provided object to JSON /// using . /// - /// /// The object that will be serialized to JSON using /// . /// The options to use when serializing to JSON. /// The type to use when serializing the data. If not specified, will /// be used to determine the type. + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public BinaryData(object? jsonSerializable, JsonSerializerOptions? options = default, Type? type = default) { type ??= jsonSerializable?.GetType() ?? typeof(object); @@ -177,12 +178,11 @@ private static async Task FromStreamAsync(Stream stream, bool async, /// Creates a instance by serializing the provided object using /// the . /// - /// /// The type to use when serializing the data. /// The data to use. /// The options to use when serializing to JSON. - /// /// A value representing the UTF-8 encoding of the JSON representation of . + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public static BinaryData FromObjectAsJson(T jsonSerializable, JsonSerializerOptions? options = default) { byte[] buffer = JsonSerializer.SerializeToUtf8Bytes(jsonSerializable, typeof(T), options); @@ -230,6 +230,7 @@ public override unsafe string ToString() /// converted to. /// The to use when serializing to JSON. /// The data converted to the specified type. + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public T? ToObjectFromJson(JsonSerializerOptions? options = default) { return JsonSerializer.Deserialize(_bytes.Span, options); diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml index 314469c96d777..d6aea3dbf37e4 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml @@ -1,7 +1,7 @@ - + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 0fee14684e2ea..1423025f7c169 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -1,4 +1,4 @@ - + win true @@ -494,7 +494,6 @@ - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index 4e4d730c4c188..14c1464e77a70 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -15,103 +15,121 @@ namespace System.Net.Http /// internal sealed class DiagnosticsHandler : DelegatingHandler { - /// - /// DiagnosticHandler constructor - /// - /// Inner handler: Windows or Unix implementation of HttpMessageHandler. - /// Note that DiagnosticHandler is the latest in the pipeline - public DiagnosticsHandler(HttpMessageHandler innerHandler) : base(innerHandler) - { - } + private const string Namespace = "System.Net.Http"; + private const string RequestWriteNameDeprecated = Namespace + ".Request"; + private const string ResponseWriteNameDeprecated = Namespace + ".Response"; + private const string ExceptionEventName = Namespace + ".Exception"; + private const string ActivityName = Namespace + ".HttpRequestOut"; + private const string ActivityStartName = ActivityName + ".Start"; + private const string ActivityStopName = ActivityName + ".Stop"; - 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 || Settings.s_diagnosticListener.IsEnabled()); - } + private static readonly DiagnosticListener s_diagnosticListener = new("HttpHandlerDiagnosticListener"); + private static readonly ActivitySource s_activitySource = new(Namespace); + + public static bool IsGloballyEnabled { get; } = GetEnableActivityPropagationValue(); - internal static bool IsGloballyEnabled() + private static bool GetEnableActivityPropagationValue() { - return Settings.s_activityPropagationEnabled; - } + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(Namespace + ".EnableActivityPropagation", out bool enableActivityPropagation)) + { + return enableActivityPropagation; + } - // SendAsyncCore returns already completed ValueTask for when async: false is passed. - // Internally, it calls the synchronous Send method of the base class. - protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: false, cancellationToken).AsTask().GetAwaiter().GetResult(); + // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"); + if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) + { + // Suppress Activity propagation. + return false; + } - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: true, cancellationToken).AsTask(); + // Defaults to enabling Activity propagation. + return true; + } - private async ValueTask SendAsyncCore(HttpRequestMessage request, bool async, - CancellationToken cancellationToken) + public DiagnosticsHandler(HttpMessageHandler innerHandler) : base(innerHandler) { - // HttpClientHandler is responsible to call static DiagnosticsHandler.IsEnabled() before forwarding request here. - // It will check if propagation is on (because parent Activity exists or there is a listener) or off (forcibly disabled) - // This code won't be called unless consumer unsubscribes from DiagnosticListener right after the check. - // So some requests happening right after subscription starts might not be instrumented. Similarly, - // when consumer unsubscribes, extra requests might be instrumented + Debug.Assert(IsGloballyEnabled); + } - if (request == null) + private static bool ShouldLogDiagnostics(HttpRequestMessage request, out Activity? activity) + { + if (request is null) { throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } - Activity? activity = null; - DiagnosticListener diagnosticListener = Settings.s_diagnosticListener; + activity = null; + + if (s_activitySource.HasListeners()) + { + activity = s_activitySource.CreateActivity(ActivityName, ActivityKind.Client); + } - // if there is no listener, but propagation is enabled (with previous IsEnabled() check) - // do not write any events just start/stop Activity and propagate Ids - if (!diagnosticListener.IsEnabled()) + if (activity is null) { - activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); - activity.Start(); - InjectHeaders(activity, request); + bool diagnosticListenerEnabled = s_diagnosticListener.IsEnabled(); - try + if (Activity.Current is not null || (diagnosticListenerEnabled && s_diagnosticListener.IsEnabled(ActivityName, request))) { - return async ? - await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : - base.Send(request, cancellationToken); + // If a diagnostics listener is enabled for the Activity, always create one + activity = new Activity(ActivityName); } - finally + else { - activity.Stop(); + // There is no Activity, but we may still want to use the instrumented SendAsyncCore if diagnostic listeners are interested in other events + return diagnosticListenerEnabled; } } - Guid loggingRequestId = Guid.Empty; + activity.Start(); - // There is a listener. Check if listener wants to be notified about HttpClient Activities - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityName, request)) + if (s_diagnosticListener.IsEnabled(ActivityStartName)) { - activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); + Write(ActivityStartName, new ActivityStartData(request)); + } - // Only send start event to users who subscribed for it, but start activity anyway - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityStartName)) - { - StartActivity(diagnosticListener, activity, new ActivityStartData(request)); - } - else - { - activity.Start(); - } + InjectHeaders(activity, request); + + return true; + } + + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (ShouldLogDiagnostics(request, out Activity? activity)) + { + ValueTask sendTask = SendAsyncCore(request, activity, async: false, cancellationToken); + return sendTask.IsCompleted ? + sendTask.Result : + sendTask.AsTask().GetAwaiter().GetResult(); } - // try to write System.Net.Http.Request event (deprecated) - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated)) + else { - long timestamp = Stopwatch.GetTimestamp(); - loggingRequestId = Guid.NewGuid(); - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, - new RequestData(request, loggingRequestId, timestamp)); + return base.Send(request, cancellationToken); } + } - // If we are on at all, we propagate current activity information - Activity? currentActivity = Activity.Current; - if (currentActivity != null) + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (ShouldLogDiagnostics(request, out Activity? activity)) + { + return SendAsyncCore(request, activity, async: true, cancellationToken).AsTask(); + } + else { - InjectHeaders(currentActivity, request); + return base.SendAsync(request, cancellationToken); + } + } + + private async ValueTask SendAsyncCore(HttpRequestMessage request, Activity? activity, bool async, CancellationToken cancellationToken) + { + Guid loggingRequestId = default; + + if (s_diagnosticListener.IsEnabled(RequestWriteNameDeprecated)) + { + loggingRequestId = Guid.NewGuid(); + Write(RequestWriteNameDeprecated, new RequestData(request, loggingRequestId, Stopwatch.GetTimestamp())); } HttpResponseMessage? response = null; @@ -126,52 +144,39 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : catch (OperationCanceledException) { taskStatus = TaskStatus.Canceled; - - // we'll report task status in HttpRequestOut.Stop throw; } catch (Exception ex) { - taskStatus = TaskStatus.Faulted; - - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ExceptionEventName)) + if (s_diagnosticListener.IsEnabled(ExceptionEventName)) { - // If request was initially instrumented, Activity.Current has all necessary context for logging - // Request is passed to provide some context if instrumentation was disabled and to avoid - // extensive Activity.Tags usage to tunnel request properties - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); + Write(ExceptionEventName, new ExceptionData(ex, request)); } + + taskStatus = TaskStatus.Faulted; throw; } finally { - // always stop activity if it was started - if (activity != null) + if (activity is not null) { - StopActivity(diagnosticListener, activity, new ActivityStopData( - response, - // If request is failed or cancelled, there is no response, therefore no information about request; - // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests - // and not retain all requests in Start - request, - taskStatus)); + activity.SetEndTime(DateTime.UtcNow); + + if (s_diagnosticListener.IsEnabled(ActivityStopName)) + { + Write(ActivityStopName, new ActivityStopData(response, request, taskStatus)); + } + + activity.Stop(); } - // Try to write System.Net.Http.Response event (deprecated) - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated)) + + if (s_diagnosticListener.IsEnabled(ResponseWriteNameDeprecated)) { - long timestamp = Stopwatch.GetTimestamp(); - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, - new ResponseData( - response, - loggingRequestId, - timestamp, - taskStatus)); + Write(ResponseWriteNameDeprecated, new ResponseData(response, loggingRequestId, Stopwatch.GetTimestamp(), taskStatus)); } } } - #region private - private sealed class ActivityStartData { // matches the properties selected in https://github.com/dotnet/diagnostics/blob/ffd0254da3bcc47847b1183fa5453c0877020abd/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/HttpRequestSourceConfiguration.cs#L36-L40 @@ -269,55 +274,29 @@ internal ResponseData(HttpResponseMessage? response, Guid loggingRequestId, long public override string ToString() => $"{{ {nameof(Response)} = {Response}, {nameof(LoggingRequestId)} = {LoggingRequestId}, {nameof(Timestamp)} = {Timestamp}, {nameof(RequestTaskStatus)} = {RequestTaskStatus} }}"; } - private static class Settings - { - private const string EnableActivityPropagationEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"; - private const string EnableActivityPropagationAppCtxSettingName = "System.Net.Http.EnableActivityPropagation"; - - public static readonly bool s_activityPropagationEnabled = GetEnableActivityPropagationValue(); - - private static bool GetEnableActivityPropagationValue() - { - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(EnableActivityPropagationAppCtxSettingName, out bool enableActivityPropagation)) - { - return enableActivityPropagation; - } - - // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. - string? envVar = Environment.GetEnvironmentVariable(EnableActivityPropagationEnvironmentVariableSettingName); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Suppress Activity propagation. - return false; - } - - // Defaults to enabling Activity propagation. - return true; - } - - public static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); - } - private static void InjectHeaders(Activity currentActivity, HttpRequestMessage request) { + const string TraceParentHeaderName = "traceparent"; + const string TraceStateHeaderName = "tracestate"; + const string RequestIdHeaderName = "Request-Id"; + const string CorrelationContextHeaderName = "Correlation-Context"; + if (currentActivity.IdFormat == ActivityIdFormat.W3C) { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName)) + if (!request.Headers.Contains(TraceParentHeaderName)) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(TraceParentHeaderName, currentActivity.Id); if (currentActivity.TraceStateString != null) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceStateHeaderName, currentActivity.TraceStateString); + request.Headers.TryAddWithoutValidation(TraceStateHeaderName, currentActivity.TraceStateString); } } } else { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName)) + if (!request.Headers.Contains(RequestIdHeaderName)) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(RequestIdHeaderName, currentActivity.Id); } } @@ -333,41 +312,16 @@ private static void InjectHeaders(Activity currentActivity, HttpRequestMessage r baggage.Add(new NameValueHeaderValue(WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)).ToString()); } while (e.MoveNext()); - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.CorrelationContextHeaderName, baggage); + request.Headers.TryAddWithoutValidation(CorrelationContextHeaderName, baggage); } } } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] - private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - string name, - T value) - { - diagnosticSource.Write(name, value); - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", - Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency.")] - private static Activity StartActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - Activity activity, - T? args) + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string name, T value) { - return diagnosticSource.StartActivity(activity, args); + s_diagnosticListener.Write(name, value); } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", - Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency.")] - private static void StopActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - Activity activity, - T? args) - { - diagnosticSource.StopActivity(activity, args); - } - - #endregion } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs deleted file mode 100644 index 0fa57394c1cc3..0000000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Http -{ - /// - /// Defines names of DiagnosticListener and Write events for DiagnosticHandler - /// - internal static class DiagnosticsHandlerLoggingStrings - { - public const string DiagnosticListenerName = "HttpHandlerDiagnosticListener"; - public const string RequestWriteNameDeprecated = "System.Net.Http.Request"; - public const string ResponseWriteNameDeprecated = "System.Net.Http.Response"; - - public const string ExceptionEventName = "System.Net.Http.Exception"; - public const string ActivityName = "System.Net.Http.HttpRequestOut"; - public const string ActivityStartName = "System.Net.Http.HttpRequestOut.Start"; - - public const string RequestIdHeaderName = "Request-Id"; - public const string CorrelationContextHeaderName = "Correlation-Context"; - - public const string TraceParentHeaderName = "traceparent"; - public const string TraceStateHeaderName = "tracestate"; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 2e3289643cfbe..bfb8f6cae7834 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -20,17 +20,17 @@ namespace System.Net.Http public partial class HttpClientHandler : HttpMessageHandler { private readonly HttpHandlerType _underlyingHandler; - private readonly DiagnosticsHandler? _diagnosticsHandler; + private readonly HttpMessageHandler _handler; private ClientCertificateOption _clientCertificateOptions; private volatile bool _disposed; public HttpClientHandler() { - _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled()) + _handler = _underlyingHandler = new HttpHandlerType(); + if (DiagnosticsHandler.IsGloballyEnabled) { - _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); + _handler = new DiagnosticsHandler(_handler); } ClientCertificateOptions = ClientCertificateOption.Manual; } @@ -288,21 +288,11 @@ public SslProtocols SslProtocols public IDictionary Properties => _underlyingHandler.Properties; [UnsupportedOSPlatform("browser")] - protected internal override HttpResponseMessage Send(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.Send(request, cancellationToken) : - _underlyingHandler.Send(request, cancellationToken); - } + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => + _handler.Send(request, cancellationToken); - protected internal override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.SendAsync(request, cancellationToken) : - _underlyingHandler.SendAsync(request, cancellationToken); - } + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + _handler.SendAsync(request, cancellationToken); // lazy-load the validator func so it can be trimmed by the ILLinker if it isn't used. private static Func? s_dangerousAcceptAnyServerCertificateValidator; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 4b00bb31f652a..63427bb96b535 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -173,7 +173,7 @@ public async Task SendAsync(CancellationToken cancellationT // See Http2Connection.SendAsync for a full comment on this logic -- it is identical behavior. if (sendContentTask.IsCompleted || _request.Content?.AllowDuplex != true || - sendContentTask == await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) || + await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == sendContentTask || sendContentTask.IsCompleted) { try diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 1fb6fd925fd33..66b60e800b8a7 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Tracing; +using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Net.Test.Common; @@ -256,7 +257,7 @@ public void SendAsync_ExpectedDiagnosticCancelledLogging() GetProperty(kvp.Value, "Request"); TaskStatus status = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Canceled, status); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -344,7 +345,7 @@ public void SendAsync_ExpectedDiagnosticSourceActivityLogging(ActivityIdFormat i activityStopResponseLogged = GetProperty(kvp.Value, "Response"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -409,7 +410,7 @@ public void SendAsync_ExpectedDiagnosticSourceActivityLogging_InvalidBaggage() Assert.Contains("goodkey=bad%2Fvalue", correlationContext); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -467,7 +468,7 @@ public void SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteHea Assert.False(request.Headers.TryGetValues("traceparent", out var _)); Assert.False(request.Headers.TryGetValues("tracestate", out var _)); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -519,7 +520,7 @@ public void SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteW3C } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Stop")) { - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -608,7 +609,7 @@ public void SendAsync_ExpectedDiagnosticExceptionActivityLogging() GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -647,7 +648,7 @@ public void SendAsync_ExpectedDiagnosticSynchronousExceptionActivityLogging() GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -796,44 +797,6 @@ public void SendAsync_ExpectedDiagnosticExceptionOnlyActivityLogging() }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListener() - { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - Activity parent = new Activity("parent").Start(); - - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertHeadersAreInjected(requestData, parent); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); - } - - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListenerOrParentActivity() - { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertNoHeadersAreInjected(requestData); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); - } - [ConditionalTheory(nameof(EnableActivityPropagationEnvironmentVariableIsNotSetAndRemoteExecutorSupported))] [InlineData("true")] [InlineData("1")] @@ -899,6 +862,111 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).Dispose(); } + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(true, true, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, false, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, null, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, true, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, false, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, null, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, true, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(true, false, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(true, null, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(false, true, true)] // DiagnosticListener requested an Activity and ActivitySource created an Activity + [InlineData(false, true, false)] // DiagnosticListener requested an Activity, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(false, true, null)] // DiagnosticListener requested an Activity, ActivitySource had no listeners, so an Activity was manually created + [InlineData(false, false, true)] // No Activity is set, DiagnosticListener does not want one, but ActivitySource created an Activity + [InlineData(false, false, false)] // No Activity is set, DiagnosticListener does not want one and ActivitySource chose not to create an Activity + [InlineData(false, false, null)] // No Activity is set, DiagnosticListener does not want one and ActivitySource has no listeners + [InlineData(false, null, true)] // No Activity is set, there is no DiagnosticListener, but ActivitySource created an Activity + [InlineData(false, null, false)] // No Activity is set, there is no DiagnosticListener and ActivitySource chose not to create an Activity + [InlineData(false, null, null)] // No Activity is set, there is no DiagnosticListener and ActivitySource has no listeners + public void SendAsync_ActivityIsCreatedIfRequested(bool currentActivitySet, bool? diagnosticListenerActivityEnabled, bool? activitySourceCreatesActivity) + { + string parameters = $"{currentActivitySet},{diagnosticListenerActivityEnabled},{activitySourceCreatesActivity}"; + + RemoteExecutor.Invoke(async (useVersion, testAsync, parametersString) => + { + bool?[] parameters = parametersString.Split(',').Select(p => p.Length == 0 ? (bool?)null : bool.Parse(p)).ToArray(); + bool currentActivitySet = parameters[0].Value; + bool? diagnosticListenerActivityEnabled = parameters[1]; + bool? activitySourceCreatesActivity = parameters[2]; + + bool madeASamplingDecision = false; + if (activitySourceCreatesActivity.HasValue) + { + ActivitySource.AddActivityListener(new ActivityListener + { + ShouldListenTo = _ => true, + Sample = (ref ActivityCreationOptions _) => + { + madeASamplingDecision = true; + return activitySourceCreatesActivity.Value ? ActivitySamplingResult.AllData : ActivitySamplingResult.None; + } + }); + } + + bool listenerCallbackWasCalled = false; + IDisposable listenerSubscription = new MemoryStream(); // Dummy disposable + if (diagnosticListenerActivityEnabled.HasValue) + { + var diagnosticListenerObserver = new FakeDiagnosticListenerObserver(_ => listenerCallbackWasCalled = true); + + diagnosticListenerObserver.Enable(name => !name.Contains("HttpRequestOut") || diagnosticListenerActivityEnabled.Value); + + listenerSubscription = DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver); + } + + Activity activity = currentActivitySet ? new Activity("parent").Start() : null; + + if (!currentActivitySet) + { + // Listen to new activity creations if an Activity was created without a parent + // (when a DiagnosticListener forced one to be created) + ActivitySource.AddActivityListener(new ActivityListener + { + ShouldListenTo = _ => true, + ActivityStarted = created => + { + Assert.Null(activity); + activity = created; + } + }); + } + + using (listenerSubscription) + { + await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(useVersion, testAsync, uri); + }, + async server => + { + HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); + + if (currentActivitySet || diagnosticListenerActivityEnabled == true || activitySourceCreatesActivity == true) + { + Assert.NotNull(activity); + AssertHeadersAreInjected(requestData, activity); + } + else + { + AssertNoHeadersAreInjected(requestData); + + if (!currentActivitySet) + { + Assert.Null(activity); + } + } + }); + } + + Assert.Equal(activitySourceCreatesActivity.HasValue, madeASamplingDecision); + Assert.Equal(diagnosticListenerActivityEnabled.HasValue, listenerCallbackWasCalled); + }, UseVersion.ToString(), TestAsync.ToString(), parameters).Dispose(); + } + private static T GetProperty(object obj, string propertyName) { Type t = obj.GetType(); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 9902e490ce5fb..d5c429466ae3c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2876,14 +2876,18 @@ await LoopbackServerFactory.CreateClientAndServerAsync( using HttpClient client = CreateHttpClient(handler); - HttpRequestException hre = await Assert.ThrowsAnyAsync(async () => await client.SendAsync(requestMessage)); + HttpRequestException hre = + await Assert.ThrowsAnyAsync(async () => await client.SendAsync(requestMessage)) + .WaitAsync(TimeSpan.FromSeconds(10)); + Assert.Equal(e, hre.InnerException); }, async server => { try { - await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); + await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo") + .WaitAsync(TimeSpan.FromSeconds(15)); } catch { } }, options: options); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index 821794e9f82d5..24b8ce369e0fa 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Test.Common; @@ -35,35 +36,119 @@ public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, st return; } - await LoopbackServerFactory.CreateClientAndServerAsync( - async uri => - { - using LoopbackSocksServer proxy = useAuth ? LoopbackSocksServer.Create("DOTNET", "424242") : LoopbackSocksServer.Create(); - using HttpClientHandler handler = CreateHttpClientHandler(); - using HttpClient client = CreateHttpClient(handler); + List<(int, long)> times = new List<(int, long)>(); + Stopwatch s = Stopwatch.StartNew(); - handler.Proxy = new WebProxy($"{scheme}://127.0.0.1:{proxy.Port}"); - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; - - if (useAuth) + try + { + times.Add((0, s.ElapsedMilliseconds)); + await LoopbackServerFactory.CreateClientAndServerAsync( + async uri => + { + times.Add((10, s.ElapsedMilliseconds)); + LoopbackSocksServer proxy = useAuth ? LoopbackSocksServer.Create("DOTNET", "424242") : LoopbackSocksServer.Create(); + try + { + times.Add((11, s.ElapsedMilliseconds)); + HttpClientHandler handler = CreateHttpClientHandler(); + try + { + times.Add((12, s.ElapsedMilliseconds)); + HttpClient client = CreateHttpClient(handler); + try + { + times.Add((13, s.ElapsedMilliseconds)); + + handler.Proxy = new WebProxy($"{scheme}://127.0.0.1:{proxy.Port}"); + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + + if (useAuth) + { + handler.Proxy.Credentials = new NetworkCredential("DOTNET", "424242"); + } + + uri = new UriBuilder(uri) { Host = host }.Uri; + + HttpRequestMessage request = CreateRequest(HttpMethod.Get, uri, UseVersion, exactVersion: true); + + times.Add((14, s.ElapsedMilliseconds)); + + HttpResponseMessage response = await client.SendAsync(TestAsync, request); + try + { + times.Add((15, s.ElapsedMilliseconds)); + string responseString = await response.Content.ReadAsStringAsync(); + times.Add((16, s.ElapsedMilliseconds)); + Assert.Equal("Echo", responseString); + times.Add((17, s.ElapsedMilliseconds)); + } + catch + { + times.Add((43, s.ElapsedMilliseconds)); + throw; + } + finally + { + times.Add((36, s.ElapsedMilliseconds)); + response.Dispose(); + times.Add((37, s.ElapsedMilliseconds)); + } + } + catch + { + times.Add((42, s.ElapsedMilliseconds)); + throw; + } + finally + { + times.Add((34, s.ElapsedMilliseconds)); + client.Dispose(); + times.Add((35, s.ElapsedMilliseconds)); + } + } + catch + { + times.Add((41, s.ElapsedMilliseconds)); + throw; + } + finally + { + times.Add((32, s.ElapsedMilliseconds)); + //handler.Dispose(); + times.Add((33, s.ElapsedMilliseconds)); + } + } + catch + { + times.Add((40, s.ElapsedMilliseconds)); + throw; + } + finally + { + times.Add((30, s.ElapsedMilliseconds)); + proxy.Dispose(); + times.Add((31, s.ElapsedMilliseconds)); + } + }, + async server => + { + times.Add((20, s.ElapsedMilliseconds)); + await server.HandleRequestAsync(content: "Echo"); + times.Add((21, s.ElapsedMilliseconds)); + }, + options: new GenericLoopbackOptions { - handler.Proxy.Credentials = new NetworkCredential("DOTNET", "424242"); - } - - uri = new UriBuilder(uri) { Host = host }.Uri; - - HttpRequestMessage request = CreateRequest(HttpMethod.Get, uri, UseVersion, exactVersion: true); - - using HttpResponseMessage response = await client.SendAsync(TestAsync, request); - string responseString = await response.Content.ReadAsStringAsync(); - Assert.Equal("Echo", responseString); - }, - async server => await server.HandleRequestAsync(content: "Echo"), - options: new GenericLoopbackOptions - { - UseSsl = useSsl, - Address = host == "::1" ? IPAddress.IPv6Loopback : IPAddress.Loopback - }); + UseSsl = useSsl, + Address = host == "::1" ? IPAddress.IPv6Loopback : IPAddress.Loopback + }, + times: times, + s: s); + } + catch (Exception ex) + { + times.Add((1, s.ElapsedMilliseconds)); + throw new Exception(string.Join('\n', times.Select(t => $"{t.Item1,2} {t.Item2}")), ex); + } } public static IEnumerable TestExceptionalAsync_MemberData() diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs index 28d301649683a..dca98cd90ee68 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.IO; using System.Threading; -using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; namespace System.Net.Sockets { @@ -248,7 +248,7 @@ internal SocketError DoOperationSendPackets(Socket socket, SafeSocketHandle hand { Debug.Assert(_sendPacketsElements != null); SendPacketsElement[] elements = (SendPacketsElement[])_sendPacketsElements.Clone(); - FileStream[] files = new FileStream[elements.Length]; + SafeFileHandle[] fileHandles = new SafeFileHandle[elements.Length]; // Open all files synchronously ahead of time so that any exceptions are propagated // to the caller, to match Windows behavior. @@ -259,14 +259,14 @@ internal SocketError DoOperationSendPackets(Socket socket, SafeSocketHandle hand string? path = elements[i]?.FilePath; if (path != null) { - files[i] = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, useAsync: true); + fileHandles[i] = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.Asynchronous); } } } catch (Exception exc) { // Clean up any files that were already opened. - foreach (FileStream s in files) + foreach (SafeFileHandle s in fileHandles) { s?.Dispose(); } @@ -288,7 +288,7 @@ internal SocketError DoOperationSendPackets(Socket socket, SafeSocketHandle hand throw; } - SocketPal.SendPacketsAsync(socket, SendPacketsFlags, elements, files, cancellationToken, (bytesTransferred, error) => + SocketPal.SendPacketsAsync(socket, SendPacketsFlags, elements, fileHandles, cancellationToken, (bytesTransferred, error) => { if (error == SocketError.Success) { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index d8d59e91a2df7..b22efcee8d904 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using Microsoft.Win32.SafeHandles; namespace System.Net.Sockets { @@ -71,7 +72,7 @@ private enum AsyncProcessingState : byte private Internals.SocketAddress? _pinnedSocketAddress; // SendPacketsElements property variables. - private FileStream[]? _sendPacketsFileStreams; + private SafeFileHandle[]? _sendPacketsFileHandles; // Overlapped object related variables. private PreAllocatedOverlapped _preAllocatedOverlapped; @@ -702,7 +703,7 @@ internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeSocketHand { // Loop through the elements attempting to open each files and get its handle. int index = 0; - _sendPacketsFileStreams = new FileStream[sendPacketsElementsFileCount]; + _sendPacketsFileHandles = new SafeFileHandle[sendPacketsElementsFileCount]; try { foreach (SendPacketsElement spe in sendPacketsElementsCopy) @@ -710,8 +711,8 @@ internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeSocketHand if (spe?.FilePath != null) { // Create a FileStream to open the file. - _sendPacketsFileStreams[index] = - new FileStream(spe.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); + _sendPacketsFileHandles[index] = + File.OpenHandle(spe.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); // Get the file handle from the stream. index++; @@ -722,8 +723,8 @@ internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeSocketHand { // Got an exception opening a file - close any open streams, then throw. for (int i = index - 1; i >= 0; i--) - _sendPacketsFileStreams[i].Dispose(); - _sendPacketsFileStreams = null; + _sendPacketsFileHandles[i].Dispose(); + _sendPacketsFileHandles = null; throw; } } @@ -1018,7 +1019,7 @@ private unsafe Interop.Winsock.TransmitPacketsElement[] SetupPinHandlesSendPacke else if (spe.FilePath != null) { // This element is a file. - sendPacketsDescriptorPinned[descriptorIndex].fileHandle = _sendPacketsFileStreams![fileIndex].SafeFileHandle.DangerousGetHandle(); + sendPacketsDescriptorPinned[descriptorIndex].fileHandle = _sendPacketsFileHandles![fileIndex].DangerousGetHandle(); sendPacketsDescriptorPinned[descriptorIndex].fileOffset = spe.OffsetLong; sendPacketsDescriptorPinned[descriptorIndex].length = (uint)spe.Count; sendPacketsDescriptorPinned[descriptorIndex].flags = @@ -1239,14 +1240,14 @@ private unsafe void FinishOperationReceiveMessageFrom() private void FinishOperationSendPackets() { // Close the files if open. - if (_sendPacketsFileStreams != null) + if (_sendPacketsFileHandles != null) { - for (int i = 0; i < _sendPacketsFileStreams.Length; i++) + for (int i = 0; i < _sendPacketsFileHandles.Length; i++) { - _sendPacketsFileStreams[i]?.Dispose(); + _sendPacketsFileHandles[i]?.Dispose(); } - _sendPacketsFileStreams = null; + _sendPacketsFileHandles = null; } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index 7006ff63a0a9d..31936b78a3d45 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -1907,10 +1907,10 @@ public static SocketError Shutdown(SafeSocketHandle handle, bool isConnected, bo return GetSocketErrorForErrorCode(err); } - private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, long offset, long count, CancellationToken cancellationToken, Action callback) + private static SocketError SendFileAsync(SafeSocketHandle handle, SafeFileHandle fileHandle, long offset, long count, CancellationToken cancellationToken, Action callback) { long bytesSent; - SocketError socketError = handle.AsyncContext.SendFileAsync(fileStream.SafeFileHandle, offset, count, out bytesSent, callback, cancellationToken); + SocketError socketError = handle.AsyncContext.SendFileAsync(fileHandle, offset, count, out bytesSent, callback, cancellationToken); if (socketError == SocketError.Success) { callback(bytesSent, SocketError.Success); @@ -1919,13 +1919,13 @@ private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fil } public static async void SendPacketsAsync( - Socket socket, TransmitFileOptions options, SendPacketsElement[] elements, FileStream[] files, CancellationToken cancellationToken, Action callback) + Socket socket, TransmitFileOptions options, SendPacketsElement[] elements, SafeFileHandle[] fileHandles, CancellationToken cancellationToken, Action callback) { SocketError error = SocketError.Success; long bytesTransferred = 0; try { - Debug.Assert(elements.Length == files.Length); + Debug.Assert(elements.Length == fileHandles.Length); for (int i = 0; i < elements.Length; i++) { SendPacketsElement e = elements[i]; @@ -1933,19 +1933,21 @@ public static async void SendPacketsAsync( { if (e.MemoryBuffer != null) { - bytesTransferred += await socket.SendAsync(e.MemoryBuffer.Value, SocketFlags.None, cancellationToken).ConfigureAwait(false); + bytesTransferred = await socket.SendAsync(e.MemoryBuffer.Value, SocketFlags.None, cancellationToken).ConfigureAwait(false) + bytesTransferred; } else { - FileStream fs = files[i] ?? e.FileStream!; - if (e.Count > fs.Length - e.OffsetLong) + SafeFileHandle fileHandle = fileHandles[i] ?? e.FileStream!.SafeFileHandle; + long fsLength = RandomAccess.GetLength(fileHandle); + + if (e.Count > fsLength - e.OffsetLong) { throw new ArgumentOutOfRangeException(); } var tcs = new TaskCompletionSource(); - error = SendFileAsync(socket.InternalSafeHandle, fs, e.OffsetLong, - e.Count > 0 ? e.Count : fs.Length - e.OffsetLong, + error = SendFileAsync(socket.InternalSafeHandle, fileHandle, e.OffsetLong, + e.Count > 0 ? e.Count : fsLength - e.OffsetLong, cancellationToken, (transferred, se) => { @@ -1975,7 +1977,7 @@ public static async void SendPacketsAsync( } catch (Exception exc) { - foreach (FileStream fs in files) + foreach (SafeFileHandle fs in fileHandles) { fs?.Dispose(); } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs index 9666c4ef1e594..cd4e18e7486db 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs @@ -44,7 +44,7 @@ internal sealed unsafe class OverlappedValueTaskSource : IValueTaskSource, internal static readonly IOCompletionCallback s_ioCallback = IOCallback; internal readonly PreAllocatedOverlapped _preallocatedOverlapped; - private readonly SafeFileHandle _fileHandle; + internal readonly SafeFileHandle _fileHandle; internal MemoryHandle _memoryHandle; internal ManualResetValueTaskSourceCore _source; // mutable struct; do not make this readonly private NativeOverlapped* _overlapped; 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 a7102ac1d3b7c..1ce14b82abe99 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 @@ -12,6 +12,7 @@ public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid { // not using bool? as it's not thread safe private volatile NullableBool _canSeek = NullableBool.Undefined; + private bool _deleteOnClose; public SafeFileHandle() : this(ownsHandle: true) { @@ -36,6 +37,7 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int { Debug.Assert(path != null); SafeFileHandle handle = Interop.Sys.Open(path, flags, mode); + handle._path = path; if (handle.IsInvalid) { @@ -51,7 +53,7 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int bool isDirectory = (error.Error == Interop.Error.ENOENT) && ((flags & Interop.Sys.OpenFlags.O_CREAT) != 0 - || !DirectoryExists(Path.GetDirectoryName(Path.TrimEndingDirectorySeparator(path!))!)); + || !DirectoryExists(System.IO.Path.GetDirectoryName(System.IO.Path.TrimEndingDirectorySeparator(path!))!)); Interop.CheckIo( error.Error, @@ -62,26 +64,31 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int // Make sure it's not a directory; we do this after opening it once we have a file descriptor // to avoid race conditions. - Interop.Sys.FileStatus status; - if (Interop.Sys.FStat(handle, out status) != 0) + // + // We can omit the check when write access is requested. open will have failed with EISDIR. + if ((flags & (Interop.Sys.OpenFlags.O_WRONLY | Interop.Sys.OpenFlags.O_RDWR)) == 0) { - Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); - handle.Dispose(); - throw Interop.GetExceptionForIoErrno(error, path); - } - if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) - { - handle.Dispose(); - throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true); - } + Interop.Sys.FileStatus status; + if (Interop.Sys.FStat(handle, out status) != 0) + { + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + handle.Dispose(); + throw Interop.GetExceptionForIoErrno(error, path); + } + if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) + { + handle.Dispose(); + throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true); + } - if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) - { - // we take advantage of the information provided by the fstat syscall - // and for regular files (most common case) - // avoid one extra sys call for determining whether file can be seeked - handle._canSeek = NullableBool.True; - Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0); + if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) + { + // we take advantage of the information provided by the fstat syscall + // and for regular files (most common case) + // avoid one extra sys call for determining whether file can be seeked + handle._canSeek = NullableBool.True; + Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0); + } } return handle; @@ -117,6 +124,17 @@ protected override bool ReleaseHandle() // problem trying to unlock it.) Interop.Sys.FLock(handle, Interop.Sys.LockOperations.LOCK_UN); // ignore any errors + // If DeleteOnClose was requested when constructed, delete the file now. + // (Unix doesn't directly support DeleteOnClose, so we mimic it here.) + if (_deleteOnClose) + { + // Since we still have the file open, this will end up deleting + // it (assuming we're the only link to it) once it's closed, but the + // name will be removed immediately. + Debug.Assert(_path is not null); + Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist + } + // Close the descriptor. Although close is documented to potentially fail with EINTR, we never want // to retry, as the descriptor could actually have been closed, been subsequently reassigned, and // be in use elsewhere in the process. Instead, we simply check whether the call was successful. @@ -234,6 +252,7 @@ private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mo private void Init(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) { IsAsync = (options & FileOptions.Asynchronous) != 0; + _deleteOnClose = (options & FileOptions.DeleteOnClose) != 0; // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory, diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index a9265207f691f..651b49be0226d 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -103,6 +103,7 @@ private static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } + fileHandle._path = fullPath; fileHandle._fileOptions = options; return fileHandle; } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs index 6f15fb45ee938..4745be0fa0155 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs @@ -7,9 +7,13 @@ namespace Microsoft.Win32.SafeHandles { public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid { + private string? _path; + public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) { SetHandle(preexistingHandle); } + + internal string? Path => _path; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs index f6da2fd101d1c..3834bbeeffe78 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs @@ -730,7 +730,7 @@ private async ValueTask ReadFromUnderlyingStreamAsync( // If the requested read is larger than buffer size, avoid the buffer and still use a single read: if (buffer.Length >= _bufferSize) { - return bytesAlreadySatisfied + await _stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + return await _stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false) + bytesAlreadySatisfied; } // Ok. We can fill the buffer: diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index 8bf4539c46f5d..bc3a04f2f5d97 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -17,10 +17,10 @@ public static partial class RandomAccess // that get stackalloced in the Linux kernel. private const int IovStackThreshold = 8; - internal static long GetFileLength(SafeFileHandle handle, string? path) + internal static long GetFileLength(SafeFileHandle handle) { int result = Interop.Sys.FStat(handle, out Interop.Sys.FileStatus status); - FileStreamHelpers.CheckFileCall(result, path); + FileStreamHelpers.CheckFileCall(result, handle.Path); return status.Size; } @@ -29,7 +29,7 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { int result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset); - FileStreamHelpers.CheckFileCall(result, path: null); + FileStreamHelpers.CheckFileCall(result, handle.Path); return result; } } @@ -64,7 +64,7 @@ internal static unsafe long ReadScatterAtOffset(SafeFileHandle handle, IReadOnly } } - return FileStreamHelpers.CheckFileCall(result, path: null); + return FileStreamHelpers.CheckFileCall(result, handle.Path); } private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) @@ -79,7 +79,7 @@ internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 139f929f1fcf4..20dcbdeac8e77 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 @@ -16,23 +16,23 @@ public static partial class RandomAccess { private static readonly IOCompletionCallback s_callback = AllocateCallback(); - internal static unsafe long GetFileLength(SafeFileHandle handle, string? path) + internal static unsafe long GetFileLength(SafeFileHandle handle) { Interop.Kernel32.FILE_STANDARD_INFO info; if (!Interop.Kernel32.GetFileInformationByHandleEx(handle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) { - throw Win32Marshal.GetExceptionForLastWin32Error(path); + throw Win32Marshal.GetExceptionForLastWin32Error(handle.Path); } return info.EndOfFile; } - internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer, long fileOffset, string? path = null) + internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer, long fileOffset) { if (handle.IsAsync) { - return ReadSyncUsingAsyncHandle(handle, buffer, fileOffset, path); + return ReadSyncUsingAsyncHandle(handle, buffer, fileOffset); } NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); @@ -55,12 +55,12 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. return 0; default: - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } - private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span buffer, long fileOffset, string? path) + private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span buffer, long fileOffset) { handle.EnsureThreadPoolBindingInitialized(); @@ -105,7 +105,7 @@ private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span buffer, long fileOffset, string? path = null) + internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { if (handle.IsAsync) { - return WriteSyncUsingAsyncHandle(handle, buffer, fileOffset, path); + return WriteSyncUsingAsyncHandle(handle, buffer, fileOffset); } NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); @@ -141,12 +141,12 @@ internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset, string? path) + private static unsafe int WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { handle.EnsureThreadPoolBindingInitialized(); @@ -193,7 +193,7 @@ private static unsafe int WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadO throw new IOException(SR.IO_FileTooLong); default: - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } @@ -469,7 +469,7 @@ private static unsafe ValueTask ReadFileScatterAsync(SafeFileHandle handle, default: // Error. Callback will not be called. vts.Dispose(); - return ValueTask.FromException(Win32Marshal.GetExceptionForWin32Error(errorCode)); + return ValueTask.FromException(Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path)); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs index 2426fb3807a2e..4d768cad53eda 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs @@ -23,7 +23,7 @@ public static long GetLength(SafeFileHandle handle) { ValidateInput(handle, fileOffset: 0); - return GetFileLength(handle, path: null); + return GetFileLength(handle); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index 7ff0e00919109..9329c0fa60b13 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -95,7 +95,7 @@ private Exception HandleIOError(long positionBefore, int errorCode) _filePosition = positionBefore; } - return SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, _path); + return SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, _fileHandle.Path); } public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush @@ -131,7 +131,7 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc try { await FileStreamHelpers - .AsyncModeCopyToAsync(_fileHandle, _path, CanSeek, _filePosition, destination, bufferSize, cancellationToken) + .AsyncModeCopyToAsync(_fileHandle, CanSeek, _filePosition, destination, bufferSize, cancellationToken) .ConfigureAwait(false); } finally diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 2228993f48e5a..22fb3e0c14022 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -472,7 +472,7 @@ private async ValueTask ReadAsyncSlowPath(Task semaphoreLockTask, Memory= _bufferSize) { - return bytesAlreadySatisfied + await _strategy.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + return await _strategy.ReadAsync(buffer, cancellationToken).ConfigureAwait(false) + bytesAlreadySatisfied; } // Ok. We can fill the buffer: diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 087c51f6f2dc0..17c03b7f93186 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -60,15 +60,15 @@ private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, internal static FileStreamStrategy EnableBufferingIfNeeded(WindowsFileStreamStrategy strategy, int bufferSize) => bufferSize > 1 ? new BufferedFileStreamStrategy(strategy, bufferSize) : strategy; - internal static void FlushToDisk(SafeFileHandle handle, string? path) + internal static void FlushToDisk(SafeFileHandle handle) { if (!Interop.Kernel32.FlushFileBuffers(handle)) { - throw Win32Marshal.GetExceptionForLastWin32Error(path); + throw Win32Marshal.GetExceptionForLastWin32Error(handle.Path); } } - internal static long Seek(SafeFileHandle handle, string? path, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + internal static long Seek(SafeFileHandle handle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); @@ -76,11 +76,11 @@ internal static long Seek(SafeFileHandle handle, string? path, long offset, Seek { if (closeInvalidHandle) { - throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(handle), path); + throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(handle), handle.Path); } else { - throw Win32Marshal.GetExceptionForLastWin32Error(path); + throw Win32Marshal.GetExceptionForLastWin32Error(handle.Path); } } @@ -116,7 +116,7 @@ internal static int GetLastWin32ErrorAndDisposeHandleIfInvalid(SafeFileHandle ha return errorCode; } - internal static void Lock(SafeFileHandle handle, string? path, long position, long length) + internal static void Lock(SafeFileHandle handle, long position, long length) { int positionLow = unchecked((int)(position)); int positionHigh = unchecked((int)(position >> 32)); @@ -125,11 +125,11 @@ internal static void Lock(SafeFileHandle handle, string? path, long position, lo if (!Interop.Kernel32.LockFile(handle, positionLow, positionHigh, lengthLow, lengthHigh)) { - throw Win32Marshal.GetExceptionForLastWin32Error(path); + throw Win32Marshal.GetExceptionForLastWin32Error(handle.Path); } } - internal static void Unlock(SafeFileHandle handle, string? path, long position, long length) + internal static void Unlock(SafeFileHandle handle, long position, long length) { int positionLow = unchecked((int)(position)); int positionHigh = unchecked((int)(position >> 32)); @@ -138,7 +138,7 @@ internal static void Unlock(SafeFileHandle handle, string? path, long position, if (!Interop.Kernel32.UnlockFile(handle, positionLow, positionHigh, lengthLow, lengthHigh)) { - throw Win32Marshal.GetExceptionForLastWin32Error(path); + throw Win32Marshal.GetExceptionForLastWin32Error(handle.Path); } } @@ -168,7 +168,7 @@ internal static void ValidateFileTypeForNonExtendedPaths(SafeFileHandle handle, } } - internal static unsafe void SetFileLength(SafeFileHandle handle, string? path, long length) + internal static unsafe void SetFileLength(SafeFileHandle handle, long length) { var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO { @@ -184,7 +184,7 @@ internal static unsafe void SetFileLength(SafeFileHandle handle, string? path, l int errorCode = Marshal.GetLastPInvokeError(); if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_FileLengthTooBig); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } @@ -215,7 +215,7 @@ internal static unsafe int ReadFileNative(SafeFileHandle handle, Span byte } } - internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? path, bool canSeek, long filePosition, Stream destination, int bufferSize, CancellationToken cancellationToken) + internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, bool canSeek, long filePosition, Stream destination, int bufferSize, CancellationToken cancellationToken) { // For efficiency, we avoid creating a new task and associated state for each asynchronous read. // Instead, we create a single reusable awaitable object that will be triggered when an await completes @@ -310,7 +310,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? p break; default: // Everything else is an error (and there won't be a callback). - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } @@ -327,7 +327,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? p case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); default: // error - throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, path); + throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, handle.Path); } // Successful operation. If we got zero bytes, we're done: exit the read/write loop. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs index 5ee047869c9a6..6c225186a3b4b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs @@ -66,7 +66,7 @@ public override long Length get { // Get the length of the file as reported by the OS - long length = RandomAccess.GetFileLength(_fileHandle, _path); + long length = RandomAccess.GetFileLength(_fileHandle); // But we may have buffered some data to be written that puts our length // beyond what the OS is aware of. Update accordingly. @@ -98,16 +98,6 @@ protected override void Dispose(bool disposing) // e.g. if this stream is wrapping a pipe and the pipe is now broken. } - // If DeleteOnClose was requested when constructed, delete the file now. - // (Unix doesn't directly support DeleteOnClose, so we mimic it here.) - if (_path != null && (_options & FileOptions.DeleteOnClose) != 0) - { - // Since we still have the file open, this will end up deleting - // it (assuming we're the only link to it) once it's closed, but the - // name will be removed immediately. - Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist - } - // Closing the file handle can fail, e.g. due to out of disk space // Throw these errors as exceptions when disposing if (_fileHandle != null && !_fileHandle.IsClosed && disposing) @@ -118,7 +108,7 @@ protected override void Dispose(bool disposing) if (SafeFileHandle.t_lastCloseErrorInfo != null) { - throw Interop.GetExceptionForIoErrno(SafeFileHandle.t_lastCloseErrorInfo.GetValueOrDefault(), _path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(SafeFileHandle.t_lastCloseErrorInfo.GetValueOrDefault(), _fileHandle.Path, isDirectory: false); } } } @@ -166,7 +156,7 @@ private void FlushOSBuffer() // doesn't support synchronization. In such cases there's nothing to flush. break; default: - throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(errorInfo, _fileHandle.Path, isDirectory: false); } } } @@ -614,14 +604,14 @@ private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, Debug.Assert(fileHandle.CanSeek); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); - long pos = FileStreamHelpers.CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin), _path); // SeekOrigin values are the same as Interop.libc.SeekWhence values + long pos = FileStreamHelpers.CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin), fileHandle.Path); // SeekOrigin values are the same as Interop.libc.SeekWhence values _filePosition = pos; return pos; } private int CheckFileCall(int result, bool ignoreNotSupported = false) { - FileStreamHelpers.CheckFileCall(result, _path, ignoreNotSupported); + FileStreamHelpers.CheckFileCall(result, _fileHandle?.Path, ignoreNotSupported); return result; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs index addfebcadaccb..3218baa33e773 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs @@ -99,7 +99,7 @@ public unsafe override long Length { get { - long len = RandomAccess.GetFileLength(_fileHandle, _path); + long len = RandomAccess.GetFileLength(_fileHandle); // If we're writing near the end of the file, we must include our // internal buffer in our Length calculation. Don't flush because @@ -179,7 +179,7 @@ public override async ValueTask DisposeAsync() } } - private void FlushOSBuffer() => FileStreamHelpers.FlushToDisk(_fileHandle, _path); + private void FlushOSBuffer() => FileStreamHelpers.FlushToDisk(_fileHandle); // Returns a task that flushes the internal write buffer private Task FlushWriteAsync(CancellationToken cancellationToken) @@ -266,7 +266,7 @@ private unsafe void SetLengthCore(long value) Debug.Assert(value >= 0, "value >= 0"); VerifyOSHandlePosition(); - FileStreamHelpers.SetFileLength(_fileHandle, _path, value); + FileStreamHelpers.SetFileLength(_fileHandle, value); if (_filePosition > value) { @@ -374,7 +374,7 @@ private unsafe int ReadNative(Span buffer) if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle)); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); } } Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); @@ -473,7 +473,7 @@ private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, { Debug.Assert(fileHandle.CanSeek, "fileHandle.CanSeek"); - return _filePosition = FileStreamHelpers.Seek(fileHandle, _path, offset, origin, closeInvalidHandle); + return _filePosition = FileStreamHelpers.Seek(fileHandle, offset, origin, closeInvalidHandle); } partial void OnBufferAllocated() @@ -578,7 +578,7 @@ private unsafe void WriteCore(ReadOnlySpan source) // to a handle opened asynchronously. if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) throw new IOException(SR.IO_FileTooLongOrHandleNotSync); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); } } Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); @@ -776,7 +776,7 @@ private unsafe Task ReadNativeAsync(Memory destination, int numBuffer } else { - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING @@ -978,7 +978,7 @@ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, Cancella } else { - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING @@ -1100,7 +1100,7 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc try { await FileStreamHelpers - .AsyncModeCopyToAsync(_fileHandle, _path, canSeek, _filePosition, destination, bufferSize, cancellationToken) + .AsyncModeCopyToAsync(_fileHandle, canSeek, _filePosition, destination, bufferSize, cancellationToken) .ConfigureAwait(false); } finally @@ -1113,8 +1113,8 @@ await FileStreamHelpers } } - internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, _path, position, length); + internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, position, length); - internal override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, _path, position, length); + internal override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, position, length); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs index 9a88f5a65d489..f01791ba9639e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs @@ -19,9 +19,6 @@ internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy /// Whether the file is opened for reading, writing, or both. private readonly FileAccess _access; - /// The path to the opened file. - private readonly string? _path; - /// The next available byte to be read from the _buffer. private int _readPos; @@ -82,7 +79,6 @@ internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess acc { string fullPath = Path.GetFullPath(path); - _path = fullPath; _access = access; _bufferLength = bufferSize; @@ -293,7 +289,7 @@ internal override SafeFileHandle SafeFileHandle } } - internal override string Name => _path ?? SR.IO_UnknownFileName; + internal override string Name => _fileHandle.Path ?? SR.IO_UnknownFileName; internal override bool IsAsync => _useAsyncIO; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs index dda1c62111975..f03c07fd4d0a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs @@ -11,7 +11,6 @@ namespace System.IO.Strategies internal abstract class WindowsFileStreamStrategy : FileStreamStrategy { protected readonly SafeFileHandle _fileHandle; // only ever null if ctor throws - protected readonly string? _path; // The path to the opened file. private readonly FileAccess _access; // What file was opened for. private readonly FileShare _share; @@ -32,7 +31,7 @@ internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, Fil { // given strategy was created out of existing handle, so we have to perform // a syscall to get the current handle offset - _filePosition = FileStreamHelpers.Seek(handle, _path, 0, SeekOrigin.Current); + _filePosition = FileStreamHelpers.Seek(handle, 0, SeekOrigin.Current); } else { @@ -46,7 +45,6 @@ internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access { string fullPath = Path.GetFullPath(path); - _path = fullPath; _access = access; _share = share; @@ -80,12 +78,12 @@ public unsafe sealed override long Length { if (_share > FileShare.Read || _exposedHandle) { - return RandomAccess.GetFileLength(_fileHandle, _path); + return RandomAccess.GetFileLength(_fileHandle); } if (_length < 0) { - _length = RandomAccess.GetFileLength(_fileHandle, _path); + _length = RandomAccess.GetFileLength(_fileHandle); } return _length; @@ -115,7 +113,7 @@ public override long Position set => _filePosition = value; } - internal sealed override string Name => _path ?? SR.IO_UnknownFileName; + internal sealed override string Name => _fileHandle.Path ?? SR.IO_UnknownFileName; internal sealed override bool IsClosed => _fileHandle.IsClosed; @@ -130,7 +128,7 @@ internal sealed override SafeFileHandle SafeFileHandle { // Update the file offset before exposing it since it's possible that // in memory position is out-of-sync with the actual file position. - FileStreamHelpers.Seek(_fileHandle, _path, _filePosition, SeekOrigin.Begin); + FileStreamHelpers.Seek(_fileHandle, _filePosition, SeekOrigin.Begin); } _exposedHandle = true; @@ -179,7 +177,7 @@ internal sealed override void Flush(bool flushToDisk) { if (flushToDisk && CanWrite) { - FileStreamHelpers.FlushToDisk(_fileHandle, _path); + FileStreamHelpers.FlushToDisk(_fileHandle); } } @@ -218,9 +216,9 @@ public sealed override long Seek(long offset, SeekOrigin origin) return pos; } - internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, _path, position, length); + internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, position, length); - internal sealed override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, _path, position, length); + internal sealed override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, position, length); private void Init(FileMode mode, string originalPath) { @@ -249,7 +247,7 @@ protected unsafe void SetLengthCore(long value) { Debug.Assert(value >= 0, "value >= 0"); - FileStreamHelpers.SetFileLength(_fileHandle, _path, value); + FileStreamHelpers.SetFileLength(_fileHandle, value); _length = value; if (_filePosition > value) @@ -273,7 +271,7 @@ private unsafe int ReadSpan(Span destination) ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } - int r = RandomAccess.ReadAtOffset(_fileHandle, destination, _filePosition, _path); + int r = RandomAccess.ReadAtOffset(_fileHandle, destination, _filePosition); Debug.Assert(r >= 0, $"RandomAccess.ReadAtOffset returned {r}."); _filePosition += r; @@ -296,7 +294,7 @@ private unsafe void WriteSpan(ReadOnlySpan source) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - int r = RandomAccess.WriteAtOffset(_fileHandle, source, _filePosition, _path); + int r = RandomAccess.WriteAtOffset(_fileHandle, source, _filePosition); Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); _filePosition += r; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs index 58f61e4cd2ed7..77f452577fc14 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs @@ -641,6 +641,21 @@ public WaitedListNode? Next } } + // Like Next, but skip nodes registered on the same thread. + public WaitedListNode? NextThread + { + get + { + s_lock.VerifyIsLocked(); + WaitedListNode? ret = _next; + while (ret != null && ReferenceEquals(ret._waitInfo, _waitInfo)) + { + ret = ret._next; + } + return ret; + } + } + public void RegisterWait(WaitableObject waitableObject) { s_lock.VerifyIsLocked(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs index f5f37471440f7..cbce78aaa5c72 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs @@ -611,7 +611,7 @@ private void SignalManualResetEvent() waiterNode = nextWaiterNode) { // Signaling a waiter will unregister the waiter node, so keep the next node before trying - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false); } @@ -635,7 +635,7 @@ private void SignalAutoResetEvent() { // Signaling a waiter will unregister the waiter node, but it may only abort the wait without satisfying the // wait, in which case we would try to signal another waiter. So, keep the next node before trying. - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; if (waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false)) { @@ -689,7 +689,7 @@ public int SignalSemaphore(int count) waiterNode = nextWaiterNode) { // Signaling the waiter will unregister the waiter node, so keep the next node before trying - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; if (waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false) && --count == 0) { @@ -753,7 +753,7 @@ private void SignalMutex(bool isAbandoned) { // Signaling a waiter will unregister the waiter node, but it may only abort the wait without satisfying the // wait, in which case we would try to signal another waiter. So, keep the next node before trying. - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; ThreadWaitInfo waitInfo = waiterNode.WaitInfo; if (waitInfo.TrySignalToSatisfyWait(waiterNode, isAbandoned)) diff --git a/src/libraries/System.Reflection/tests/AssemblyTests.cs b/src/libraries/System.Reflection/tests/AssemblyTests.cs index 0732615ec9e02..6c5c8c2bb320b 100644 --- a/src/libraries/System.Reflection/tests/AssemblyTests.cs +++ b/src/libraries/System.Reflection/tests/AssemblyTests.cs @@ -510,7 +510,7 @@ public void Location_ExecutingAssembly_IsNotNull() } #pragma warning disable SYSLIB0012 - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] // single file public void CodeBase() { Assert.NotEmpty(Helpers.ExecutingAssembly.CodeBase); diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs index 0dc17c595d8ae..3dda623337f8b 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs @@ -23,30 +23,36 @@ public class ToBase64Transform : ICryptoTransform // converting to Base64 takes 3 bytes input and generates 4 bytes output public int InputBlockSize => 3; public int OutputBlockSize => 4; - public bool CanTransformMultipleBlocks => false; + public bool CanTransformMultipleBlocks => true; public virtual bool CanReuseTransform => true; public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - // inputCount < InputBlockSize is not allowed - ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount, InputBlockSize); + ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); + + int inputBlocks = Math.DivRem(inputCount, InputBlockSize, out int inputRemainder); + + if (inputBlocks == 0) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); if (outputBuffer == null) ThrowHelper.ThrowArgumentNull(ThrowHelper.ExceptionArgument.outputBuffer); - // For now, only convert 3 bytes to 4 - Span input = inputBuffer.AsSpan(inputOffset, InputBlockSize); - Span output = outputBuffer.AsSpan(outputOffset, OutputBlockSize); + if (inputRemainder != 0) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); - OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: false); + int requiredOutputLength = checked(inputBlocks * OutputBlockSize); + if (requiredOutputLength > outputBuffer.Length - outputOffset) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.outputBuffer); - if (written != OutputBlockSize) - { - ThrowHelper.ThrowCryptographicException(); - } + Span input = inputBuffer.AsSpan(inputOffset, inputCount); + Span output = outputBuffer.AsSpan(outputOffset, requiredOutputLength); + + OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: false); Debug.Assert(status == OperationStatus.Done); - Debug.Assert(consumed == InputBlockSize); + Debug.Assert(consumed == input.Length); + Debug.Assert(written == output.Length); return written; } @@ -56,28 +62,20 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input // inputCount <= InputBlockSize is allowed ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - // Convert.ToBase64CharArray already does padding, so all we have to check is that - // the inputCount wasn't 0 + // Convert.ToBase64CharArray already does padding, so all we have to check is that the inputCount wasn't 0 if (inputCount == 0) - { return Array.Empty(); - } - else if (inputCount > InputBlockSize) - { - ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); - } - // Again, for now only a block at a time Span input = inputBuffer.AsSpan(inputOffset, inputCount); - byte[] output = new byte[OutputBlockSize]; - OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: true); + int inputBlocks = Math.DivRem(inputCount, InputBlockSize, out int inputRemainder); + int outputBlocks = inputBlocks + (inputRemainder != 0 ? 1 : 0); - if (written != OutputBlockSize) - { - ThrowHelper.ThrowCryptographicException(); - } + byte[] output = new byte[outputBlocks * OutputBlockSize]; + OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: true); + + Debug.Assert(written == output.Length); Debug.Assert(status == OperationStatus.Done); Debug.Assert(consumed == inputCount); @@ -390,14 +388,6 @@ public static void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, i ThrowInvalidOffLen(); } - public static void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, int inputBlockSize) - { - ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - - if (inputCount < inputBlockSize) - ThrowArgumentOutOfRange(ExceptionArgument.inputCount); - } - [DoesNotReturn] public static void ThrowArgumentNull(ExceptionArgument argument) => throw new ArgumentNullException(argument.ToString()); [DoesNotReturn] diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs index aab283c32c543..1a6cc5ed1824e 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics; using System.IO; +using System.Linq; using Xunit; namespace System.Security.Cryptography.Encoding.Tests @@ -56,34 +56,29 @@ public static IEnumerable TestData_Oversize() [Fact] public void InvalidInput_ToBase64Transform() { - byte[] data_5bytes = Text.Encoding.ASCII.GetBytes("aaaaa"); + byte[] data_3bytes = Text.Encoding.ASCII.GetBytes("aaa"); + ICryptoTransform transform = new ToBase64Transform(); - using (var transform = new ToBase64Transform()) - { - InvalidInput_Base64Transform(transform); + AssertExtensions.Throws("inputBuffer", () => transform.TransformBlock(null, 0, 0, null, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformBlock(Array.Empty(), -1, 0, null, 0)); + AssertExtensions.Throws("outputBuffer", () => transform.TransformBlock(data_3bytes, 0, 3, null, 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(Array.Empty(), 0, 1, null, 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(data_3bytes, 0, 1, new byte[10], 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(new byte[4], 0, 4, new byte[10], 0)); + AssertExtensions.Throws("outputBuffer", () => transform.TransformBlock(data_3bytes, 0, 3, new byte[1], 0)); + AssertExtensions.Throws(null, () => transform.TransformBlock(Array.Empty(), 1, 0, null, 0)); - // These exceptions only thrown in ToBase - AssertExtensions.Throws("inputCount", () => transform.TransformFinalBlock(data_5bytes, 0, 5)); - } + AssertExtensions.Throws("inputBuffer", () => transform.TransformFinalBlock(null, 0, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); + AssertExtensions.Throws(null, () => transform.TransformFinalBlock(Array.Empty(), 1, 0)); } [Fact] public void InvalidInput_FromBase64Transform() { byte[] data_4bytes = Text.Encoding.ASCII.GetBytes("aaaa"); - ICryptoTransform transform = new FromBase64Transform(); - InvalidInput_Base64Transform(transform); - - // These exceptions only thrown in FromBase - transform.Dispose(); - Assert.Throws(() => transform.TransformBlock(data_4bytes, 0, 4, null, 0)); - Assert.Throws(() => transform.TransformFinalBlock(Array.Empty(), 0, 0)); - } - - private void InvalidInput_Base64Transform(ICryptoTransform transform) - { - byte[] data_4bytes = Text.Encoding.ASCII.GetBytes("aaaa"); AssertExtensions.Throws("inputBuffer", () => transform.TransformBlock(null, 0, 0, null, 0)); AssertExtensions.Throws("inputOffset", () => transform.TransformBlock(Array.Empty(), -1, 0, null, 0)); @@ -95,6 +90,28 @@ private void InvalidInput_Base64Transform(ICryptoTransform transform) AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); AssertExtensions.Throws(null, () => transform.TransformFinalBlock(Array.Empty(), 1, 0)); + + // These exceptions only thrown in FromBase + transform.Dispose(); + Assert.Throws(() => transform.TransformBlock(data_4bytes, 0, 4, null, 0)); + Assert.Throws(() => transform.TransformFinalBlock(Array.Empty(), 0, 0)); + } + + [Fact] + public void ToBase64_TransformFinalBlock_MatchesConvert() + { + for (int i = 0; i < 100; i++) + { + byte[] input = new byte[i]; + Random.Shared.NextBytes(input); + + string expected = Convert.ToBase64String(input); + + using var transform = new ToBase64Transform(); + string actual = string.Concat(transform.TransformFinalBlock(input, 0, input.Length).Select(b => char.ToString((char)b))); + + Assert.Equal(expected, actual); + } } [Theory, MemberData(nameof(TestData_Ascii))] @@ -115,6 +132,38 @@ public static void ValidateFromBase64CryptoStream(string data, string encoding) } } + [Theory] + [InlineData(100)] + [InlineData(101)] + [InlineData(102)] + [InlineData(103)] + public static void RoundtripCryptoStream(int length) + { + byte[] expected = RandomNumberGenerator.GetBytes(length); + var ms = new MemoryStream(); + + using (var toBase64 = new ToBase64Transform()) + using (var stream = new CryptoStream(ms, toBase64, CryptoStreamMode.Write, leaveOpen: true)) + { + stream.Write(expected); + } + + ms.Position = 0; + + byte[] actual = new byte[expected.Length]; + using (var fromBase64 = new FromBase64Transform()) + using (var stream = new CryptoStream(ms, fromBase64, CryptoStreamMode.Read, leaveOpen: true)) + { + int totalRead = 0, bytesRead; + while ((bytesRead = stream.Read(actual.AsSpan(totalRead))) != 0) + { + totalRead += bytesRead; + } + Assert.Equal(actual.Length, totalRead); + AssertExtensions.SequenceEqual(expected, actual); + } + } + private static void ValidateCryptoStream(string expected, string data, ICryptoTransform transform) { byte[] inputBytes = Text.Encoding.ASCII.GetBytes(data); @@ -148,9 +197,10 @@ public static void ValidateToBase64TransformFinalBlock(string data, string expec byte[] inputBytes = Text.Encoding.ASCII.GetBytes(data); Assert.True(inputBytes.Length > 4); - // Test passing blocks > 4 characters to TransformFinalBlock (not supported) - _ = expected; - AssertExtensions.Throws("inputCount", () => transform.TransformFinalBlock(inputBytes, 0, inputBytes.Length)); + // Test passing blocks > 4 characters to TransformFinalBlock (supported) + byte[] outputBytes = transform.TransformFinalBlock(inputBytes, 0, inputBytes.Length); + string outputString = Text.Encoding.ASCII.GetString(outputBytes, 0, outputBytes.Length); + Assert.Equal(expected, outputString); } } @@ -280,7 +330,7 @@ public void TransformUsageFlags_ToBase64Transform() { using (var transform = new ToBase64Transform()) { - Assert.False(transform.CanTransformMultipleBlocks); + Assert.True(transform.CanTransformMultipleBlocks); Assert.True(transform.CanReuseTransform); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs index 254a0dad8ca07..4fae4694bd417 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs @@ -14,7 +14,7 @@ namespace Internal.Cryptography.Pal internal sealed partial class AppleCertificatePal : ICertificatePal { // Byte representation of "-----BEGIN " - private static byte[] pemBegin = new byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20 }; + private static ReadOnlySpan PemBegin => new byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20 }; internal delegate bool DerCallback(ReadOnlySpan derData, X509ContentType contentType); @@ -30,7 +30,7 @@ internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCal // Look for the PEM marker. This doesn't guarantee it will be a valid PEM since we don't check whether // the marker is at the beginning of line or whether the line is a complete marker. It's just a quick // check to avoid conversion from bytes to characters if the content is DER encoded. - if (rawData.IndexOf(pemBegin) < 0) + if (rawData.IndexOf(PemBegin) < 0) { return false; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 77fd99c01a69e..8e9db6ed69585 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -988,11 +988,17 @@ void CheckChain() // Clear UntrustedRoot, if it happened. allFlags &= ~X509ChainStatusFlags.UntrustedRoot; - Assert.Equal( - X509ChainStatusFlags.NotSignatureValid, - allFlags); - - Assert.Equal(3, chain.ChainElements.Count); + // The chain result can either be PartialChain or NotSignatureValid. + // If the flags are PartialChain, then move on. + // If the flags are not PartialChain, we make sure the result is + // NotSignatureValid. + // In the case of PartialChain, we don't care how many certificates + // are in the chain. + if (allFlags != X509ChainStatusFlags.PartialChain) + { + Assert.Equal(X509ChainStatusFlags.NotSignatureValid, allFlags); + Assert.Equal(3, chain.ChainElements.Count); + } Assert.False(valid, $"Chain is valid on execution {iter}"); } diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 5a6753eb34312..67a09866d7445 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -903,6 +903,7 @@ public static partial class JsonMetadataServices public static System.Text.Json.Serialization.JsonConverter SByteConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter SingleConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter StringConverter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter TimeSpanConverter { get { throw null; } } [System.CLSCompliantAttribute(false)] public static System.Text.Json.Serialization.JsonConverter UInt16Converter { get { throw null; } } [System.CLSCompliantAttribute(false)] diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 9f73cb0ec2956..c650f6ebad8bb 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -318,6 +318,9 @@ The JSON value is not in a supported DateTimeOffset format. + + The JSON value is not in a supported TimeSpan format. + The JSON value is not in a supported Guid format. diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 96038fb8d8686..190cbd485342a 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -165,6 +165,7 @@ + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs index a4dc832223df1..7f05dfafc5173 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs @@ -117,12 +117,6 @@ public static bool IsValidDateTimeOffsetParseLength(int length) return IsInRangeInclusive(length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidDateTimeOffsetParseLength(long length) - { - return IsInRangeInclusive(length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - } - /// /// Parse the given UTF-8 as extended ISO 8601 format. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs index 32947d9f090ae..0af169f2e1df7 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs @@ -1073,25 +1073,25 @@ internal bool TryGetDateTimeCore(out DateTime value) { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(sequenceLength)) + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(ValueSpan.Length)) + if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; @@ -1141,25 +1141,25 @@ internal bool TryGetDateTimeOffsetCore(out DateTimeOffset value) { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(sequenceLength)) + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(ValueSpan.Length)) + if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; @@ -1210,24 +1210,25 @@ internal bool TryGetGuidCore(out Guid value) { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedGuidLength : JsonConstants.MaximumFormatGuidLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - if (sequenceLength > JsonConstants.MaximumEscapedGuidLength) + if (sequenceLength > maximumLength) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedGuidLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedGuidLength : JsonConstants.MaximumFormatGuidLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (ValueSpan.Length > JsonConstants.MaximumEscapedGuidLength) + if (ValueSpan.Length > maximumLength) { value = default; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs new file mode 100644 index 0000000000000..3767b7ff6d451 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Buffers.Text; +using System.Diagnostics; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class TimeSpanConverter : JsonConverter + { + private const int MinimumTimeSpanFormatLength = 8; // hh:mm:ss + private const int MaximumTimeSpanFormatLength = 26; // -dddddddd.hh:mm:ss.fffffff + private const int MaximumEscapedTimeSpanFormatLength = JsonConstants.MaxExpansionFactorWhileEscaping * MaximumTimeSpanFormatLength; + + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw ThrowHelper.GetInvalidOperationException_ExpectedString(reader.TokenType); + } + + bool isEscaped = reader._stringHasEscaping; + int maximumLength = isEscaped ? MaximumEscapedTimeSpanFormatLength : MaximumTimeSpanFormatLength; + + ReadOnlySpan source = stackalloc byte[0]; + + if (reader.HasValueSequence) + { + ReadOnlySequence valueSequence = reader.ValueSequence; + long sequenceLength = valueSequence.Length; + + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, MinimumTimeSpanFormatLength, maximumLength)) + { + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + Span stackSpan = stackalloc byte[isEscaped ? MaximumEscapedTimeSpanFormatLength : MaximumTimeSpanFormatLength]; + valueSequence.CopyTo(stackSpan); + source = stackSpan.Slice(0, (int)sequenceLength); + } + else + { + source = reader.ValueSpan; + + if (!JsonHelpers.IsInRangeInclusive(source.Length, MinimumTimeSpanFormatLength, maximumLength)) + { + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + } + + if (isEscaped) + { + int backslash = source.IndexOf(JsonConstants.BackSlash); + Debug.Assert(backslash != -1); + + Span sourceUnescaped = stackalloc byte[source.Length]; + + JsonReaderHelper.Unescape(source, sourceUnescaped, backslash, out int written); + Debug.Assert(written > 0); + + source = sourceUnescaped.Slice(0, written); + Debug.Assert(!source.IsEmpty); + } + + byte firstChar = source[0]; + if (!JsonHelpers.IsDigit(firstChar) && firstChar != '-') + { + // Note: Utf8Parser.TryParse allows for leading whitespace so we + // need to exclude that case here. + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + bool result = Utf8Parser.TryParse(source, out TimeSpan tmpValue, out int bytesConsumed, 'c'); + + // Note: Utf8Parser.TryParse will return true for invalid input so + // long as it starts with an integer. Example: "2021-06-18" or + // "1$$$$$$$$$$". We need to check bytesConsumed to know if the + // entire source was actually valid. + + if (result && source.Length == bytesConsumed) + { + return tmpValue; + } + + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + { + Span output = stackalloc byte[MaximumTimeSpanFormatLength]; + + bool result = Utf8Formatter.TryFormat(value, output, out int bytesWritten, 'c'); + Debug.Assert(result); + + writer.WriteStringValue(output.Slice(0, bytesWritten)); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index 95de3062e667f..a5270028def60 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -49,7 +49,7 @@ private void RootBuiltInConverters() private static Dictionary GetDefaultSimpleConverters() { - const int NumberOfSimpleConverters = 23; + const int NumberOfSimpleConverters = 24; var converters = new Dictionary(NumberOfSimpleConverters); // Use a dictionary for simple converters. @@ -72,6 +72,7 @@ private static Dictionary GetDefaultSimpleConverters() Add(JsonMetadataServices.SByteConverter); Add(JsonMetadataServices.SingleConverter); Add(JsonMetadataServices.StringConverter); + Add(JsonMetadataServices.TimeSpanConverter); Add(JsonMetadataServices.UInt16Converter); Add(JsonMetadataServices.UInt32Converter); Add(JsonMetadataServices.UInt64Converter); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs index 0be5273425cdb..f51bc851a0a53 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs @@ -110,6 +110,12 @@ public static partial class JsonMetadataServices public static JsonConverter StringConverter => s_stringConverter ??= new StringConverter(); private static JsonConverter? s_stringConverter; + /// + /// Returns a instance that converts values. + /// + public static JsonConverter TimeSpanConverter => s_timeSpanConverter ??= new TimeSpanConverter(); + private static JsonConverter? s_timeSpanConverter; + /// /// Returns a instance that converts values. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index 2b6b85878c9ae..96296fa7b2fed 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -636,6 +636,9 @@ public static FormatException GetFormatException(DataType dateType) case DataType.DateTimeOffset: message = SR.FormatDateTimeOffset; break; + case DataType.TimeSpan: + message = SR.FormatTimeSpan; + break; case DataType.Base64String: message = SR.CannotDecodeInvalidBase64; break; @@ -723,6 +726,7 @@ internal enum DataType Boolean, DateTime, DateTimeOffset, + TimeSpan, Base64String, Guid, } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs index 799f2abd0b407..8d4efb5e3cb2a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs @@ -189,7 +189,6 @@ public static IEnumerable InvalidISO8601Tests() yield return new object[] { "\"1997-07-16T19:20:30.4555555+1400\"" }; yield return new object[] { "\"1997-07-16T19:20:30.4555555-1400\"" }; - // Proper format but invalid calendar date, time, or time zone designator fields yield return new object[] { "\"1997-00-16T19:20:30.4555555\"" }; yield return new object[] { "\"1997-07-16T25:20:30.4555555\"" }; @@ -215,6 +214,7 @@ public static IEnumerable InvalidISO8601Tests() yield return new object[] { "\"1997-07-16T19:20:30.45555555550000000\"" }; yield return new object[] { "\"1997-07-16T19:20:30.45555555555555555\"" }; yield return new object[] { "\"1997-07-16T19:20:30.45555555555555555555\"" }; + yield return new object[] { "\"1997-07-16T19:20:30.4555555555555555+01:300\"" }; // Hex strings diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs index b4a8d0db76e21..75ad7d1e6bae4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Runtime.CompilerServices; +using Newtonsoft.Json; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -80,6 +81,7 @@ public static void ReadPrimitivesFail() Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); + Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); @@ -118,6 +120,7 @@ public static void ReadPrimitivesFail() [InlineData(typeof(sbyte))] [InlineData(typeof(float))] [InlineData(typeof(string))] + [InlineData(typeof(TimeSpan))] [InlineData(typeof(ushort))] [InlineData(typeof(uint))] [InlineData(typeof(ulong))] @@ -303,6 +306,9 @@ public static void ValueFail() Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize("1")); Assert.Throws(() => JsonSerializer.Deserialize("1")); @@ -442,5 +448,70 @@ private static void DeserializeLongJsonString(int stringLength) string str = JsonSerializer.Deserialize(json); Assert.True(json.AsSpan(1, json.Length - 2).SequenceEqual(str.AsSpan())); } + + [Theory] + [InlineData("23:59:59")] + [InlineData("\\u002D23:59:59", "-23:59:59")] + [InlineData("\\u0032\\u0033\\u003A\\u0035\\u0039\\u003A\\u0035\\u0039", "23:59:59")] + [InlineData("23:59:59.9", "23:59:59.9000000")] + [InlineData("23:59:59.9999999")] + [InlineData("9999999.23:59:59.9999999")] + [InlineData("-9999999.23:59:59.9999999")] + [InlineData("10675199.02:48:05.4775807")] // TimeSpan.MaxValue + [InlineData("-10675199.02:48:05.4775808")] // TimeSpan.MinValue + public static void TimeSpan_Read_Success(string json, string? actual = null) + { + TimeSpan value = JsonSerializer.Deserialize($"\"{json}\""); + + Assert.Equal(TimeSpan.Parse(actual ?? json), value); + Assert.Equal(value, JsonConvert.DeserializeObject($"\"{json}\"")); + } + + [Fact] + public static void TimeSpan_Read_KnownDifferences() + { + string value = "24:00:00"; + + // 24:00:00 should be invalid because hours can only be up to 23. + Assert.Throws(() => JsonSerializer.Deserialize($"\"{value}\"")); + + TimeSpan expectedValue = TimeSpan.Parse("24.00:00:00"); + + // TimeSpan.Parse has a quirk where it treats 24:00:00 as 24.00:00:00. + Assert.Equal(expectedValue, TimeSpan.Parse(value)); + + // Newtonsoft uses TimeSpan.Parse so it is subject to the quirk. + Assert.Equal(expectedValue, JsonConvert.DeserializeObject($"\"{value}\"")); + } + + [Theory] + [InlineData("\t23:59:59")] // Otherwise valid but has invalid json character + [InlineData("\\t23:59:59")] // Otherwise valid but has leading whitespace + [InlineData("23:59:59 ")] // Otherwise valid but has trailing whitespace + [InlineData("24:00:00")] + [InlineData("\\u0032\\u0034\\u003A\\u0030\\u0030\\u003A\\u0030\\u0030")] + [InlineData("00:60:00")] + [InlineData("00:00:60")] + [InlineData("00:00:00.00000009")] + [InlineData("900000000.00:00:00")] + [InlineData("1:00:00")] // 'g' Format + [InlineData("1:2:00:00")] // 'g' Format + [InlineData("+00:00:00")] + [InlineData("2021-06-18")] + [InlineData("1$")] + [InlineData("10675199.02:48:05.4775808")] // TimeSpan.MaxValue + 1 + [InlineData("-10675199.02:48:05.4775809")] // TimeSpan.MinValue - 1 + [InlineData("1234", false)] + [InlineData("{}", false)] + [InlineData("[]", false)] + [InlineData("true", false)] + [InlineData("null", false)] + public static void TimeSpan_Read_Failure(string json, bool addQuotes = true) + { + if (addQuotes) + json = $"\"{json}\""; + + Assert.Throws(() => JsonSerializer.Deserialize(json)); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs index 210cc6fbde566..f3e6bd8d3a447 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs @@ -112,5 +112,24 @@ public static void WritePrimitives() Assert.Equal(@"""1.2.3.4""", JsonSerializer.Serialize(version)); } } + + [Theory] + [InlineData("1:59:59", "01:59:59")] + [InlineData("23:59:59")] + [InlineData("23:59:59.9", "23:59:59.9000000")] + [InlineData("23:59:59.9999999")] + [InlineData("1.23:59:59")] + [InlineData("9999999.23:59:59.9999999")] + [InlineData("-9999999.23:59:59.9999999")] + [InlineData("10675199.02:48:05.4775807")] // TimeSpan.MaxValue + [InlineData("-10675199.02:48:05.4775808")] // TimeSpan.MinValue + public static void TimeSpan_Write_Success(string value, string? expectedValue = null) + { + TimeSpan ts = TimeSpan.Parse(value); + string json = JsonSerializer.Serialize(ts); + + Assert.Equal($"\"{expectedValue ?? value}\"", json); + Assert.Equal(json, JsonConvert.SerializeObject(ts)); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs index 1df529baf9503..bd4cbdb864e77 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Globalization; using Xunit; @@ -201,5 +202,55 @@ public static void TestingDateTimeMaxValue() // Test upstream serializer. Assert.Equal(DateTime.Parse(expectedString), JsonSerializer.Deserialize(jsonString)); } + + [Theory] + [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] + public static void TryGetDateTime_HasValueSequence_False(string testString) + { + static void test(string testString, bool isFinalBlock) + { + byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); + ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); + var json = new Utf8JsonReader(sequence, isFinalBlock: isFinalBlock, state: default); + + Assert.True(json.Read(), "json.Read()"); + Assert.Equal(JsonTokenType.String, json.TokenType); + Assert.True(json.HasValueSequence, "json.HasValueSequence"); + // If the string is empty, the ValueSequence is empty, because it contains all 0 bytes between the two characters + Assert.Equal(string.IsNullOrEmpty(testString), json.ValueSequence.IsEmpty); + Assert.False(json.TryGetDateTime(out DateTime actual), "json.TryGetDateTime(out DateTime actual)"); + Assert.Equal(DateTime.MinValue, actual); + + JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTime()); + } + + test(testString, isFinalBlock: true); + test(testString, isFinalBlock: false); + } + + [Theory] + [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] + public static void TryGetDateTimeOffset_HasValueSequence_False(string testString) + { + static void test(string testString, bool isFinalBlock) + { + byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); + ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); + var json = new Utf8JsonReader(sequence, isFinalBlock: isFinalBlock, state: default); + + Assert.True(json.Read(), "json.Read()"); + Assert.Equal(JsonTokenType.String, json.TokenType); + Assert.True(json.HasValueSequence, "json.HasValueSequence"); + // If the string is empty, the ValueSequence is empty, because it contains all 0 bytes between the two characters + Assert.Equal(string.IsNullOrEmpty(testString), json.ValueSequence.IsEmpty); + Assert.False(json.TryGetDateTimeOffset(out DateTimeOffset actual), "json.TryGetDateTimeOffset(out DateTimeOffset actual)"); + Assert.Equal(DateTimeOffset.MinValue, actual); + + JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTimeOffset()); + } + + test(testString, isFinalBlock: true); + test(testString, isFinalBlock: false); + } } } diff --git a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs index c005e2623511d..e696a38cf924e 100644 --- a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs +++ b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs @@ -322,7 +322,6 @@ public static void TaskConfigurableAwaiter() /// FromAsync testing: Not supported in .NET Native /// [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52614", TestPlatforms.iOS | TestPlatforms.tvOS)] public static void FromAsync() { Task emptyTask = new Task(() => { }); diff --git a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs index 5d4212129c175..0c31290c67a0a 100644 --- a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs +++ b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs @@ -22,6 +22,7 @@ public static partial class ThreadPool public static long PendingWorkItemCount { get { throw null; } } public static int ThreadCount { get { throw null; } } [System.ObsoleteAttribute("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static bool BindHandle(System.IntPtr osHandle) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static bool BindHandle(System.Runtime.InteropServices.SafeHandle osHandle) { throw null; } @@ -43,6 +44,7 @@ public static partial class ThreadPool public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { throw null; } public static bool SetMinThreads(int workerThreads, int completionPortThreads) { throw null; } [System.CLSCompliantAttribute(false)] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public unsafe static bool UnsafeQueueNativeOverlapped(System.Threading.NativeOverlapped* overlapped) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Threading.IThreadPoolWorkItem callBack, bool preferLocal) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Threading.WaitCallback callBack, object? state) { throw null; } diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index a893662df1519..4af1d538bfabb 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -348,7 +348,7 @@ public void AbandonExisting( } else { - Assert.True(m2Index < notAbandonedWaitIndex); + Assert.True(!isNotAbandonedWaitObjectSignaled || m2Index < notAbandonedWaitIndex); Assert.Equal(m2Index, ame.MutexIndex); } diff --git a/src/libraries/pkg/Directory.Build.props b/src/libraries/pkg/Directory.Build.props index f05c02795a651..d31c32ee6470c 100644 --- a/src/libraries/pkg/Directory.Build.props +++ b/src/libraries/pkg/Directory.Build.props @@ -16,8 +16,8 @@ - - + + amd64 $(TargetArchitecture) diff --git a/src/libraries/pkg/dir.traversal.targets b/src/libraries/pkg/dir.traversal.targets index 0cb3942d47a2e..a3dd57e19a835 100644 --- a/src/libraries/pkg/dir.traversal.targets +++ b/src/libraries/pkg/dir.traversal.targets @@ -47,18 +47,18 @@ - FilterProjectsPackageRID; + FilterProjectsOutputRid; $(TraversalBuildDependsOn); - - + + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '' and '$(SkipBuildIdentityPackage)' != 'true'" /> - <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(PackageRID)' and '$(SkipBuildRuntimePackage)' != 'true'" /> + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(OutputRid)' and '$(SkipBuildRuntimePackage)' != 'true'" /> diff --git a/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj b/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj index 8fedc510cadf1..b7b43e933dfd8 100644 --- a/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj +++ b/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj @@ -6,7 +6,7 @@ - runtimes/$(PackageRID)/native + runtimes/$(OutputRid)/native diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 038be0cbcf7cc..52a82116bc424 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -62,29 +62,6 @@ if(ENABLE_MINIMAL) process_enable_minimal() endif() -# -# This is the version of the corlib-runtime interface. When -# making changes to this interface (by changing the layout -# of classes the runtime knows about, changing icall signature or -# semantics etc), change this variable. -# -# This must be unique relative to corlib interface and semantics. -# -# If you change corlib such that a runtime change is required, or -# vice versa, change this string. Examples include removing icalls, -# adding icalls, changing icall signatures, and changing type layouts -# that both sides know. -# -# It is an arbitrary string and should be parsed as such. -# A guid works and is encouraged. -# -# There is no ordering of corlib versions, no old or new, -# an exact match is required between corlib and runtime. -# -# This line is parsed by other tools, it should remain in the format they expect. -# -set(MONO_CORLIB_VERSION 1A5E0066-58DC-428A-B21C-0AD6CDAE2789) - set(DISABLE_MDB 1) set(DISABLE_COM 1) set(DISABLE_PERFCOUNTERS 1) @@ -726,6 +703,10 @@ else() # FIXME: set(NAME_DEV_RANDOM "\"/dev/random\"") endif() + +if(HOST_LINUX AND HAVE_SYS_MMAN_H AND HAVE_ELF_H AND HAVE_SYS_SYSCALL_H) + set(ENABLE_JIT_DUMP 1) +endif() ### End of other checks ###################################### diff --git a/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets b/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets deleted file mode 100644 index b9bfab7bd6a4b..0000000000000 --- a/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets +++ /dev/null @@ -1,36 +0,0 @@ - - - - GenerateMonoCoreLibVersionFile;$(CompileDependsOn) - $(IntermediateOutputPath)\Environment.MonoCoreLibVersion.cs - - - - - <_MonoCoreLibVersionNumber>$([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MonoProjectRoot)\CMakeLists.txt')), 'MONO_CORLIB_VERSION ([a-fA-F0-9\-]+)').Groups[1].Value) - <_MonoCoreLibVersionFileLines> -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// This is a generated file. Do not edit. - -#pragma warning disable CA1823 // unused field -namespace System -{ - public partial class Environment - { - private const string mono_corlib_version = "$(_MonoCoreLibVersionNumber)"%3B - } -} - - - - - - - - - - - - diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index fd93f62380088..5290a6bc8c57f 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,7 +1,4 @@  - - - false true diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml index f332a9d01cb80..eac1bad9a0b2a 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -129,8 +129,6 @@ - - diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 5fb9e07417464..21ac12509e6be 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -253,9 +253,7 @@ private static void AddPublicNestedTypes(Type type, List types, Listdomain) : NULL; if (debug_info) { offset_entries = debug_info->num_line_numbers; - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (!events_data->buffer || needed_size > events_data->buffer_size) { - g_free (events_data->buffer); - events_data->buffer_size = (size_t)(needed_size * 1.5); - events_data->buffer = g_new (uint8_t, events_data->buffer_size); - } + if (offset_entries != 0) { + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (!events_data->buffer || needed_size > events_data->buffer_size) { + g_free (events_data->buffer); + events_data->buffer_size = (size_t)(needed_size * 1.5); + events_data->buffer = g_new (uint8_t, events_data->buffer_size); + } - if (events_data->buffer) { - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; + if (events_data->buffer) { + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } } } @@ -794,6 +824,21 @@ eventpipe_fire_method_events ( g_free (method_signature); } +static +inline +bool +include_method (MonoMethod *method) +{ + if (!method) { + return false; + } else if (!m_method_is_wrapper (method)) { + return true; + } else { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; + } +} + static void eventpipe_fire_method_events_func ( @@ -805,7 +850,7 @@ eventpipe_fire_method_events_func ( if (ji && !ji->is_trampoline && !ji->async) { MonoMethod *method = jinfo_get_method (ji); - if (method && !m_method_is_wrapper (method)) + if (include_method (method)) eventpipe_fire_method_events (ji, method, events_data); } } @@ -899,15 +944,28 @@ eventpipe_execute_rundown ( if (root_domain) { uint64_t domain_id = (uint64_t)root_domain; - // Iterate all functions in use (JIT, AOT and Interpreter). + // Emit all functions in use (JIT, AOT and Interpreter). EventPipeFireMethodEventsData events_data; events_data.domain = root_domain; events_data.buffer_size = 1024 * sizeof(uint32_t); events_data.buffer = g_new (uint8_t, events_data.buffer_size); events_data.method_events_func = method_events_func; + + // All called JIT/AOT methods should be included in jit info table. mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); + + // All called interpreted methods should be included in interpreter jit info table. if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); + + // Phantom methods injected in callstacks representing runtime functions. + if (_ep_rt_mono_runtime_helper_compile_method_jitinfo && _ep_rt_mono_runtime_helper_compile_method) + eventpipe_fire_method_events (_ep_rt_mono_runtime_helper_compile_method_jitinfo, _ep_rt_mono_runtime_helper_compile_method, &events_data); + if (_ep_rt_mono_monitor_enter_method_jitinfo && _ep_rt_mono_monitor_enter_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_method_jitinfo, _ep_rt_mono_monitor_enter_method, &events_data); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo && _ep_rt_mono_monitor_enter_v4_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_v4_method_jitinfo, _ep_rt_mono_monitor_enter_v4_method, &events_data); + g_free (events_data.buffer); // Iterate all assemblies in domain. @@ -936,17 +994,78 @@ eventpipe_execute_rundown ( return TRUE; } +inline +static +bool +in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) + return true; + + return false; +} + +inline +static +bool +in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && + (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_v4_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) + return true; + + return false; +} + static gboolean eventpipe_walk_managed_stack_for_thread ( MonoStackFrameInfo *frame, MonoContext *ctx, - void *data, - bool *async_frame, - bool *safe_point_frame) + EventPipeStackWalkData *stack_walk_data) { EP_ASSERT (frame != NULL); - EP_ASSERT (data != NULL); + EP_ASSERT (stack_walk_data != NULL); switch (frame->type) { case FRAME_TYPE_DEBUGGER_INVOKE: @@ -955,23 +1074,41 @@ eventpipe_walk_managed_stack_for_thread ( case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_JIT_ENTRY: + // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. + // Makes it possible to detect stacks waiting on JIT compiler. + if (_ep_rt_mono_runtime_helper_compile_method && stack_walk_data->top_frame) + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_runtime_helper_compile_method), _ep_rt_mono_runtime_helper_compile_method); + stack_walk_data->top_frame = false; return FALSE; case FRAME_TYPE_MANAGED: case FRAME_TYPE_INTERP: - if (!frame->ji) - return FALSE; - *async_frame |= frame->ji->async; - MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; - if (method && m_method_is_wrapper (method)) { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info(method); - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll) - *safe_point_frame = true; - } else if (method && !m_method_is_wrapper (method)) { - ep_stack_contents_append ((EventPipeStackContents *)data, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { - ep_stack_contents_append ((EventPipeStackContents *)data, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + if (frame->ji) { + stack_walk_data->async_frame |= frame->ji->async; + MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; + if (method && m_method_is_wrapper (method)) { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->safe_point_frame = true; + }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->runtime_invoke_frame = true; + } else if (_ep_rt_mono_monitor_enter_method && in_monitor_enter_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_method), _ep_rt_mono_monitor_enter_method); + } else if (_ep_rt_mono_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_v4_method), _ep_rt_mono_monitor_enter_v4_method); + } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } + } else if (method && !m_method_is_wrapper (method)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + } } - return ep_stack_contents_get_length ((EventPipeStackContents *)data) >= EP_MAX_STACK_DEPTH; + stack_walk_data->top_frame = false; + return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; default: EP_UNREACHABLE ("eventpipe_walk_managed_stack_for_thread"); return FALSE; @@ -985,9 +1122,7 @@ eventpipe_walk_managed_stack_for_thread_func ( MonoContext *ctx, void *data) { - bool async_frame = false; - bool safe_point_frame = false; - return eventpipe_walk_managed_stack_for_thread (frame, ctx, data, &async_frame, &safe_point_frame); + return eventpipe_walk_managed_stack_for_thread (frame, ctx, (EventPipeStackWalkData *)data); } static @@ -1000,7 +1135,7 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( EP_ASSERT (frame != NULL); EP_ASSERT (data != NULL); - EventPipeSampleProfileData *sample_data = (EventPipeSampleProfileData *)data; + EventPipeSampleProfileStackWalkData *sample_data = (EventPipeSampleProfileStackWalkData *)data; if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { switch (frame->type) { @@ -1011,11 +1146,11 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( case FRAME_TYPE_TRAMPOLINE: sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; break; + case FRAME_TYPE_JIT_ENTRY: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; case FRAME_TYPE_INTERP: - if (frame->managed) - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - else - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; break; case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: @@ -1025,7 +1160,7 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( } } - return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_contents, &sample_data->async_frame, &sample_data->safe_point_frame); + return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); } static @@ -1051,6 +1186,65 @@ ep_rt_mono_init (void) _ep_rt_mono_profiler = mono_profiler_create (NULL); mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_eventpipe_thread_exited); + + MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); + if (method_signature) { + method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); + method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); + + ERROR_DECL (error); + MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); + if (is_ok (error) && runtime_helpers) { + MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); + if (method_builder) { + _ep_rt_mono_runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); + mono_mb_free (method_builder); + } + } + mono_error_cleanup (error); + mono_metadata_free_method_signature (method_signature); + + _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_runtime_helper_compile_method) { + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; + _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + } + } + + { + ERROR_DECL (error); + MonoMethodDesc *desc = NULL; + MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); + if (is_ok (error) && monitor) { + desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; + } + } + + desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_method_jitinfo) { + _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); + _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; + } + } + } + mono_error_cleanup (error); + } } void @@ -1082,6 +1276,20 @@ ep_rt_mono_fini (void) if (_ep_rt_mono_initialized) mono_rand_close (_ep_rt_mono_rand_provider); + g_free (_ep_rt_mono_runtime_helper_compile_method_jitinfo); + _ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; + + mono_free_method (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_method_jitinfo); + _ep_rt_mono_monitor_enter_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_v4_method_jitinfo); + _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_v4_method = NULL; + _ep_rt_mono_sampled_thread_callstacks = NULL; _ep_rt_mono_rand_provider = NULL; _ep_rt_mono_initialized = FALSE; @@ -1126,6 +1334,41 @@ ep_rt_mono_thread_attach (bool background_thread) return thread; } +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) +{ + void *result = ep_rt_mono_thread_attach (background_thread); + if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { + // Increase sampling thread priority, accepting failures. +#ifdef HOST_WIN32 + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); +#elif _POSIX_PRIORITY_SCHEDULING + int policy; + int priority; + struct sched_param param; + int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); + if (schedparam_result == 0) { + // Attempt to switch the thread to real time scheduling. This will not + // necessarily work on all OSs; for example, most Linux systems will give + // us EPERM here unless configured to allow this. + priority = param.sched_priority; + param.sched_priority = sched_get_priority_max (SCHED_RR); + if (param.sched_priority != -1) { + schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); + if (schedparam_result != 0) { + // Fallback, attempt to increase to max priority using current policy. + param.sched_priority = sched_get_priority_max (policy); + if (param.sched_priority != -1 && param.sched_priority != priority) + pthread_setschedparam (pthread_self (), policy, ¶m); + } + } + } +#endif + } + + return result; +} + void ep_rt_mono_thread_detach (void) { @@ -1446,10 +1689,17 @@ ep_rt_mono_walk_managed_stack_for_thread ( { EP_ASSERT (thread != NULL && stack_contents != NULL); + EventPipeStackWalkData stack_walk_data; + stack_walk_data.stack_contents = stack_contents; + stack_walk_data.top_frame = true; + stack_walk_data.async_frame = false; + stack_walk_data.safe_point_frame = false; + stack_walk_data.runtime_invoke_frame = false; + if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); return true; } @@ -1503,7 +1753,7 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( // Sample profiler only runs on one thread, no need to synchorinize. if (!_ep_rt_mono_sampled_thread_callstacks) - _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileData), _ep_rt_mono_max_sampled_thread_count); + _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileStackWalkData), _ep_rt_mono_max_sampled_thread_count); // Make sure there is room based on previous max number of sampled threads. // NOTE, there is a chance there are more threads than max, if that's the case we will @@ -1524,16 +1774,22 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); if (thread_state->valid) { if (sampled_thread_count < _ep_rt_mono_max_sampled_thread_count) { - EventPipeSampleProfileData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileData, sampled_thread_count); + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, sampled_thread_count); data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; - data->async_frame = FALSE; - data->safe_point_frame = FALSE; + data->stack_walk_data.stack_contents = &data->stack_contents; + data->stack_walk_data.top_frame = true; + data->stack_walk_data.async_frame = false; + data->stack_walk_data.safe_point_frame = false; + data->stack_walk_data.runtime_invoke_frame = false; ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); - if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && data->safe_point_frame) + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { + // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame + // as second, re-classify current callstack to be executing managed code. data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } sampled_thread_count++; } @@ -1550,12 +1806,12 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( // adapter instance and only set recorded tid as parameter inside adapter. THREAD_INFO_TYPE adapter = { { 0 } }; for (uint32_t i = 0; i < sampled_thread_count; ++i) { - EventPipeSampleProfileData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileData, i); + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, i); if (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length(&data->stack_contents) > 0) { // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event // for this specific stackwalk or we could cleanup stack_frames before writing profile event. - if (data->async_frame) { + if (data->stack_walk_data.async_frame) { for (int i = 0; i < data->stack_contents.next_available_frame; ++i) mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [i], TRUE, FALSE); } @@ -1720,19 +1976,21 @@ ep_rt_mono_write_event_method_il_to_native_map ( MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; if (debug_info) { - offset_entries = debug_info->num_line_numbers; - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (needed_size > sizeof (fixed_buffer)) { - buffer = g_new (uint8_t, needed_size); - il_offsets = (uint32_t*)buffer; - } else { - il_offsets = fixed_buffer; - } - if (il_offsets) { - native_offsets = il_offsets + offset_entries; - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + if (offset_entries != 0) { + offset_entries = debug_info->num_line_numbers; + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (needed_size > sizeof (fixed_buffer)) { + buffer = g_new (uint8_t, needed_size); + il_offsets = (uint32_t*)buffer; + } else { + il_offsets = fixed_buffer; + } + if (il_offsets) { + native_offsets = il_offsets + offset_entries; + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } } } diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 22bdccdcd7df2..2c3a5f7e36d70 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -527,6 +527,15 @@ ep_rt_mono_thread_setup (bool background_thread) ep_rt_mono_thread_attach (background_thread); } +static +inline +void +ep_rt_mono_thread_setup_2 (bool background_thread, EventPipeThreadType thread_type) +{ + extern void * ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); + ep_rt_mono_thread_attach_2 (background_thread, thread_type); +} + static inline void @@ -1233,7 +1242,7 @@ EP_RT_DEFINE_THREAD_FUNC (ep_rt_thread_mono_start_func) { rt_mono_thread_params_internal_t *thread_params = (rt_mono_thread_params_internal_t *)data; - ep_rt_mono_thread_setup (thread_params->background_thread); + ep_rt_mono_thread_setup_2 (thread_params->background_thread, thread_params->thread_params.thread_type); thread_params->thread_params.thread = ep_rt_thread_get_handle (); mono_thread_start_return_t result = thread_params->thread_params.thread_func (thread_params); diff --git a/src/mono/mono/metadata/appdomain.c b/src/mono/mono/metadata/appdomain.c index 1e9b3d213839d..09d8c4527e3e1 100644 --- a/src/mono/mono/metadata/appdomain.c +++ b/src/mono/mono/metadata/appdomain.c @@ -309,37 +309,6 @@ mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoT HANDLE_FUNCTION_RETURN (); } -static char* -mono_get_corlib_version (void) -{ - ERROR_DECL (error); - - MonoClass *klass; - MonoClassField *field; - - klass = mono_class_load_from_name (mono_defaults.corlib, "System", "Environment"); - mono_class_init_internal (klass); - field = mono_class_get_field_from_name_full (klass, "mono_corlib_version", NULL); - if (!field) - return NULL; - - if (! (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_LITERAL))) - return NULL; - - char *value; - MonoTypeEnum field_type; - const char *data = mono_class_get_field_default_value (field, &field_type); - if (field_type != MONO_TYPE_STRING) - return NULL; - mono_metadata_read_constant_value (data, field_type, &value, error); - mono_error_assert_ok (error); - - char *res = mono_string_from_blob (value, error); - mono_error_assert_ok (error); - - return res; -} - /** * mono_check_corlib_version: * Checks that the corlib that is loaded matches the version of this runtime. @@ -364,18 +333,6 @@ mono_check_corlib_version_internal (void) return NULL; #else char *result = NULL; - char *version = mono_get_corlib_version (); - if (!version) { - result = g_strdup_printf ("expected corlib string (%s) but not found or not string", MONO_CORLIB_VERSION); - goto exit; - } - if (strcmp (version, MONO_CORLIB_VERSION) != 0) { - result = g_strdup_printf ("The runtime did not find the mscorlib.dll it expected. " - "Expected interface version %s but found %s. Check that " - "your runtime and class libraries are matching.", - MONO_CORLIB_VERSION, version); - goto exit; - } /* Check that the managed and unmanaged layout of MonoInternalThread matches */ guint32 native_offset; @@ -384,8 +341,6 @@ mono_check_corlib_version_internal (void) managed_offset = mono_field_get_offset (mono_class_get_field_from_name_full (mono_defaults.internal_thread_class, "last", NULL)); if (native_offset != managed_offset) result = g_strdup_printf ("expected InternalThread.last field offset %u, found %u. See InternalThread.last comment", native_offset, managed_offset); -exit: - g_free (version); return result; #endif } diff --git a/src/mono/mono/metadata/components.c b/src/mono/mono/metadata/components.c index d0a2c1257f23d..242580434fd37 100644 --- a/src/mono/mono/metadata/components.c +++ b/src/mono/mono/metadata/components.c @@ -16,17 +16,23 @@ #include "mono/utils/mono-logger-internals.h" #include "mono/utils/mono-path.h" #include "mono/utils/mono-time.h" +#include "mono/utils/refcount.h" static gint64 event_pipe_100ns_ticks; typedef MonoComponent * (*MonoComponentInitFn) (void); +typedef struct _MonoComponentLibrary { + MonoRefCount ref; + MonoDl *lib; +} MonoComponentLibrary; + typedef struct _MonoComponentEntry { const char *lib_name; const char *name; MonoComponentInitFn init; MonoComponent **component; - MonoDl *lib; + MonoComponentLibrary *lib; } MonoComponentEntry; #define COMPONENT_INIT_FUNC(name) (MonoComponentInitFn) mono_component_ ## name ## _init @@ -58,8 +64,10 @@ MonoComponentEntry components[] = { }; #ifndef STATIC_COMPONENTS +static GHashTable *component_library_load_history = NULL; + static MonoComponent* -get_component (const MonoComponentEntry *component, MonoDl **component_lib); +get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib); #endif void @@ -82,12 +90,14 @@ mono_components_init (void) for (int i = 0; i < G_N_ELEMENTS (components); ++i) *components [i].component = components [i].init (); #else + component_library_load_history = g_hash_table_new (g_str_hash, g_str_equal); + /* call get_component for each component and init it or its stubs and add it to loaded_components */ - MonoDl *lib = NULL; + MonoComponentLibrary *component_lib = NULL; for (int i = 0; i < G_N_ELEMENTS (components); ++i) { - *components [i].component = get_component (&components [i], &lib); - components [i].lib = lib; + *components [i].component = get_component (&components [i], &component_lib); + components [i].lib = component_lib; if (!*components [i].component) *components [i].component = components [i].init (); } @@ -106,11 +116,11 @@ component_init_name (const MonoComponentEntry *component) } static gpointer -load_component_entrypoint (MonoDl *lib, const MonoComponentEntry *component) +load_component_entrypoint (MonoComponentLibrary *component_lib, const MonoComponentEntry *component) { char *component_init = component_init_name (component); gpointer sym = NULL; - char *error_msg = mono_dl_symbol (lib, component_init, &sym); + char *error_msg = mono_dl_symbol (component_lib->lib, component_init, &sym); if (error_msg) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s library does not have symbol %s: %s", component->name, component_init, error_msg); g_free (error_msg); @@ -150,59 +160,76 @@ try_load (const char* dir, const MonoComponentEntry *component, const char* comp char *path = NULL; void *iter = NULL; - while ((path = mono_dl_build_path (dir, component_base_lib, &iter))) { + while (lib == NULL && (path = mono_dl_build_platform_path (dir, component_base_lib, &iter))) { char *error_msg = NULL; lib = mono_dl_open (path, MONO_DL_EAGER | MONO_DL_LOCAL, &error_msg); - if (lib) - break; if (!lib) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found: %s", component->name, error_msg); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s not found at %s: %s", component_base_lib, path, error_msg); + g_free (error_msg); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s found at %s", component_base_lib, path); } - g_free (error_msg); g_free (path); } - if (lib) - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found at %s", component->name, path); - g_free (path); + return lib; } + + static MonoComponentInitFn -load_component (const MonoComponentEntry *component, MonoDl **lib_out) +load_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out) { // If init method has been static linked not using stub library, use that instead of dynamic component. if (component->init() && component->init()->available ()) { - *lib_out = NULL; + *component_lib_out = NULL; return component->init; } char *component_base_lib = component_library_base_name (component); MonoComponentInitFn result = NULL; + MonoComponentLibrary *component_lib = NULL; - /* FIXME: just copy what mono_profiler_load does, assuming it works */ + // Check history of component library loads to reduce try_load attempts (optimization for libraries hosting multiple components). + if (!g_hash_table_lookup_extended (component_library_load_history, component_base_lib, NULL, (gpointer *)&component_lib)) { + MonoDl *lib = NULL; +#if !defined(HOST_IOS) && !defined(HOST_TVOS) && !defined(HOST_WATCHOS) && !defined(HOST_MACCAT) && !defined(HOST_ANDROID) + lib = try_load (components_dir (), component, component_base_lib); +#endif + if (!lib) + lib = try_load (NULL, component, component_base_lib); - /* FIXME: do I need to provide a path? */ - MonoDl *lib = NULL; - lib = try_load (components_dir (), component, component_base_lib); - if (!lib) - lib = try_load (NULL, component, component_base_lib); + component_lib = g_new0 (MonoComponentLibrary, 1); + if (component_lib) { + mono_refcount_init (component_lib, NULL); + component_lib->lib = lib; + } - g_free (component_base_lib); - if (!lib) + g_hash_table_insert (component_library_load_history, g_strdup (component_base_lib), (gpointer)component_lib); + } + + if (!component_lib || !component_lib->lib) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found", component->name); goto done; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found in %s", component->name, component_base_lib); - gpointer sym = load_component_entrypoint (lib, component); + gpointer sym = load_component_entrypoint (component_lib, component); result = (MonoComponentInitFn)sym; - *lib_out = lib; + + mono_refcount_inc (component_lib); + *component_lib_out = component_lib; done: + g_free (component_base_lib); return result; } MonoComponent* -get_component (const MonoComponentEntry *component, MonoDl **lib_out) +get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out) { - MonoComponentInitFn initfn = load_component (component, lib_out); + MonoComponentInitFn initfn = load_component (component, component_lib_out); if (!initfn) return NULL; return initfn(); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 84fc4df27c1b2..d725b1c3b5fd1 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -4462,12 +4462,18 @@ MonoStringHandle ves_icall_System_Reflection_RuntimeAssembly_get_code_base (MonoReflectionAssemblyHandle assembly, MonoError *error) { MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly, assembly); - gchar *absolute; - if (g_path_is_absolute (mass->image->name)) { - absolute = g_strdup (mass->image->name); + /* return NULL for bundled assemblies in single-file scenarios */ + const char* filename = m_image_get_filename (mass->image); + + if (!filename) + return NULL_HANDLE_STRING; + + gchar *absolute; + if (g_path_is_absolute (filename)) { + absolute = g_strdup (filename); } else { - absolute = g_build_filename (mass->basedir, mass->image->name, (const char*)NULL); + absolute = g_build_filename (mass->basedir, filename, (const char*)NULL); } mono_icall_make_platform_path (absolute); diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index f16c8c36f8147..fc64623fad652 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -1657,6 +1657,7 @@ stack_walk_adapter (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: return FALSE; case FRAME_TYPE_MANAGED: case FRAME_TYPE_INTERP: diff --git a/src/mono/mono/metadata/method-builder.h b/src/mono/mono/metadata/method-builder.h index d746fd3806c29..fd3903c866445 100644 --- a/src/mono/mono/metadata/method-builder.h +++ b/src/mono/mono/metadata/method-builder.h @@ -18,6 +18,7 @@ #include #include #include +#include typedef struct _MonoMethodBuilder MonoMethodBuilder; @@ -30,16 +31,16 @@ typedef struct { MonoMethod* (*create_method) (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack); } MonoMethodBuilderCallbacks; -MonoMethodBuilder * +MONO_COMPONENT_API MonoMethodBuilder * mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type); MonoMethodBuilder * mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type); -void +MONO_COMPONENT_API void mono_mb_free (MonoMethodBuilder *mb); -MonoMethod * +MONO_COMPONENT_API MonoMethod * mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack); guint32 diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 10808d2144b13..6867a07379cd8 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -493,12 +493,11 @@ static MonoDl * netcore_probe_for_module_variations (const char *mdirname, const char *file_name, int raw_flags) { void *iter = NULL; - char *full_name; + char *full_name = NULL; MonoDl *module = NULL; - // FIXME: this appears to search *.dylib twice for some reason - while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) { - char *error_msg; + while (module == NULL && (full_name = mono_dl_build_path (mdirname, file_name, &iter))) { + char *error_msg = NULL; module = mono_dl_open_full (full_name, MONO_DL_LAZY, raw_flags, &error_msg); if (!module) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg); @@ -506,7 +505,6 @@ netcore_probe_for_module_variations (const char *mdirname, const char *file_name } g_free (full_name); } - g_free (full_name); return module; } diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index d6df5b150120d..eea17726ba2e0 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -450,6 +450,8 @@ arch_unwind_frame (MonoJitTlsData *jit_tls, frame->type = FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX; memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); } + } else if (ext->kind == MONO_LMFEXT_JIT_ENTRY) { + frame->type = FRAME_TYPE_JIT_ENTRY; } else { g_assert_not_reached (); } @@ -1873,6 +1875,7 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: continue; case FRAME_TYPE_INTERP: case FRAME_TYPE_MANAGED: @@ -2278,6 +2281,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt case FRAME_TYPE_TRAMPOLINE: case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + case FRAME_TYPE_JIT_ENTRY: *ctx = new_ctx; continue; case FRAME_TYPE_INTERP_ENTRY: @@ -2722,6 +2726,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu case FRAME_TYPE_TRAMPOLINE: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: *ctx = new_ctx; continue; case FRAME_TYPE_INTERP_TO_MANAGED: diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 87f2070d38982..7ba23bd560f92 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -87,6 +87,7 @@ #include "mini-gc.h" #include "mini-llvm.h" +#include "llvm-runtime.h" #include "lldb.h" #include "mini-runtime.h" #include "interp/interp.h" @@ -2664,13 +2665,54 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_ return p; } +typedef struct { + MonoMethod *method; + guint32 opt; + gboolean jit_only; + MonoError *error; + gpointer code; +} JitCompileMethodWithOptCallbackData; + +static void +jit_compile_method_with_opt_cb (gpointer arg) +{ + JitCompileMethodWithOptCallbackData *params = (JitCompileMethodWithOptCallbackData *)arg; + params->code = mono_jit_compile_method_with_opt (params->method, params->opt, params->jit_only, params->error); +} + +static gpointer +jit_compile_method_with_opt (JitCompileMethodWithOptCallbackData *params) +{ + MonoLMFExt ext; + + memset (&ext, 0, sizeof (MonoLMFExt)); + ext.kind = MONO_LMFEXT_JIT_ENTRY; + mono_push_lmf (&ext); + + gboolean thrown = FALSE; +#if defined(ENABLE_LLVM_RUNTIME) || defined(ENABLE_LLVM) + mono_llvm_cpp_catch_exception (jit_compile_method_with_opt_cb, params, &thrown); +#else + jit_compile_method_with_opt_cb (params); +#endif + + mono_pop_lmf (&ext.lmf); + + return !thrown ? params->code : NULL; +} + gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error) { - gpointer code; + JitCompileMethodWithOptCallbackData params; - code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), FALSE, error); - return code; + params.method = method; + params.opt = mono_get_optimizations_for_method (method, default_opt); + params.jit_only = FALSE; + params.error = error; + params.code = NULL; + + return jit_compile_method_with_opt (¶ms); } /* @@ -2681,10 +2723,15 @@ mono_jit_compile_method (MonoMethod *method, MonoError *error) gpointer mono_jit_compile_method_jit_only (MonoMethod *method, MonoError *error) { - gpointer code; + JitCompileMethodWithOptCallbackData params; - code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), TRUE, error); - return code; + params.method = method; + params.opt = mono_get_optimizations_for_method (method, default_opt); + params.jit_only = TRUE; + params.error = error; + params.code = NULL; + + return jit_compile_method_with_opt (¶ms); } /* diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index 57fb1b2982466..2e349a35ed031 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -197,6 +197,7 @@ struct MonoJitTlsData { #define MONO_LMFEXT_DEBUGGER_INVOKE 1 #define MONO_LMFEXT_INTERP_EXIT 2 #define MONO_LMFEXT_INTERP_EXIT_WITH_CTX 3 +#define MONO_LMFEXT_JIT_ENTRY 4 /* * The MonoLMF structure is arch specific, it includes at least these fields. diff --git a/src/mono/mono/sgen/gc-internal-agnostic.h b/src/mono/mono/sgen/gc-internal-agnostic.h index 73d431d54d8c7..581cdb4896e1b 100644 --- a/src/mono/mono/sgen/gc-internal-agnostic.h +++ b/src/mono/mono/sgen/gc-internal-agnostic.h @@ -110,7 +110,7 @@ word aligned or size is not a multiple of word size. */ void mono_gc_bzero_atomic (void *dest, size_t size); void mono_gc_bzero_aligned (void *dest, size_t size); -void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); +MONO_COMPONENT_API void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); void mono_gc_memmove_aligned (void *dest, const void *src, size_t size); FILE *mono_gc_get_logfile (void); diff --git a/src/mono/mono/utils/mono-dl.c b/src/mono/mono/utils/mono-dl.c index eec013399de3a..166cd5d602a8f 100644 --- a/src/mono/mono/utils/mono-dl.c +++ b/src/mono/mono/utils/mono-dl.c @@ -390,35 +390,12 @@ mono_dl_close (MonoDl *module) g_free (module); } -/** - * mono_dl_build_path: - * \param directory optional directory - * \param name base name of the library - * \param iter iterator token - * Given a directory name and the base name of a library, iterate - * over the possible file names of the library, taking into account - * the possible different suffixes and prefixes on the host platform. - * - * The returned file name must be freed by the caller. - * \p iter must point to a NULL pointer the first time the function is called - * and then passed unchanged to the following calls. - * \returns the filename or NULL at the end of the iteration - */ -char* -mono_dl_build_path (const char *directory, const char *name, void **iter) -{ - int idx; - const char *prefix; - const char *suffix; - gboolean need_prefix = TRUE, need_suffix = TRUE; - int prlen; - int suffixlen; - char *res; - int iteration; - - if (!iter) - return NULL; +typedef gboolean (*dl_library_name_formatting_func)(int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix); +static +gboolean +dl_default_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ /* The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our "bootstrap" phase in which we check the passed name verbatim and only if we fail to find @@ -428,26 +405,79 @@ mono_dl_build_path (const char *directory, const char *name, void **iter) libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill here. */ - iteration = GPOINTER_TO_UINT (*iter); - idx = iteration; if (idx == 0) { /* Name */ - need_prefix = FALSE; - need_suffix = FALSE; - suffix = ""; + *need_prefix = FALSE; + *need_suffix = FALSE; + *suffix = ""; } else if (idx == 1) { /* netcore system libs have a suffix but no prefix */ - need_prefix = FALSE; - need_suffix = TRUE; - suffix = mono_dl_get_so_suffixes () [0]; - suffixlen = strlen (suffix); + *need_prefix = FALSE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [0]; } else { /* Prefix.Name.suffix */ - suffix = mono_dl_get_so_suffixes () [idx - 2]; - if (suffix [0] == '\0') - return NULL; + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [idx - 2]; + if ((*suffix) [0] == '\0') + return FALSE; + } + + return TRUE; +} + +static +gboolean +dl_reverse_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ + const char ** suffixes = mono_dl_get_so_suffixes (); + int suffix_count = 0; + + while (suffixes [suffix_count][0] != '\0') + suffix_count++; + + if (idx < suffix_count) { + /* Prefix.Name.suffix */ + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = suffixes [idx]; + } else if (idx == suffix_count) { + /* netcore system libs have a suffix but no prefix */ + *need_prefix = FALSE; + *need_suffix = TRUE; + *suffix = suffixes [0]; + } else if (idx == suffix_count + 1) { + /* Name */ + *need_prefix = FALSE; + *need_suffix = FALSE; + *suffix = ""; + } else { + return FALSE; } + return TRUE; +} + +static char* +dl_build_path (const char *directory, const char *name, void **iter, dl_library_name_formatting_func func) +{ + const char *prefix; + const char *suffix; + gboolean need_prefix = TRUE, need_suffix = TRUE; + int prlen; + int suffixlen; + char *res; + int iteration; + + if (!iter) + return NULL; + + + iteration = GPOINTER_TO_UINT (*iter); + if (!func (iteration, &need_prefix, &need_suffix, &suffix)) + return NULL; + if (need_prefix) { prlen = strlen (mono_dl_get_so_prefix ()); if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0) @@ -471,6 +501,64 @@ mono_dl_build_path (const char *directory, const char *name, void **iter) return res; } +/** + * mono_dl_build_path: + * \param directory optional directory + * \param name base name of the library + * \param iter iterator token + * Given a directory name and the base name of a library, iterate + * over the possible file names of the library, taking into account + * the possible different suffixes and prefixes on the host platform. + * + * The returned file name must be freed by the caller. + * \p iter must point to a NULL pointer the first time the function is called + * and then passed unchanged to the following calls. + * \returns the filename or NULL at the end of the iteration + */ +char* +mono_dl_build_path (const char *directory, const char *name, void **iter) +{ +#ifdef HOST_ANDROID + return dl_build_path (directory, name, iter, dl_reverse_library_name_formatting); +#else + return dl_build_path (directory, name, iter, dl_default_library_name_formatting); +#endif +} + +static +gboolean +dl_prefix_suffix_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ + /* Prefix.Name.suffix */ + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [idx]; + if ((*suffix) [0] == '\0') + return FALSE; + + return TRUE; +} + +/** + * mono_dl_build_platform_path: + * \param directory optional directory + * \param name base name of the library + * \param iter iterator token + * Given a directory name and the base name of a library, iterate + * over platform prefix and suffixes generating a library name + * suitable for the platform. Function won't try additional fallbacks. + * + * The returned file name must be freed by the caller. + * \p iter must point to a NULL pointer the first time the function is called + * and then passed unchanged to the following calls. + * \returns the filename or NULL at the end of the iteration + */ +char* +mono_dl_build_platform_path (const char *directory, const char *name, void **iter) +{ + return dl_build_path (directory, name, iter, dl_prefix_suffix_library_name_formatting); +} + MonoDlFallbackHandler * mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data) { diff --git a/src/mono/mono/utils/mono-dl.h b/src/mono/mono/utils/mono-dl.h index 44e7e9e34e186..5a810f8c295da 100644 --- a/src/mono/mono/utils/mono-dl.h +++ b/src/mono/mono/utils/mono-dl.h @@ -36,6 +36,7 @@ MONO_EXTERN_C void mono_dl_close (MonoDl *module); char* mono_dl_build_path (const char *directory, const char *name, void **iter); +char* mono_dl_build_platform_path (const char *directory, const char *name, void **iter); MonoDl* mono_dl_open_runtime_lib (const char *lib_name, int flags, char **error_msg); diff --git a/src/mono/mono/utils/mono-stack-unwinding.h b/src/mono/mono/utils/mono-stack-unwinding.h index 15335fc961926..f27b2cbb00a6a 100644 --- a/src/mono/mono/utils/mono-stack-unwinding.h +++ b/src/mono/mono/utils/mono-stack-unwinding.h @@ -30,7 +30,9 @@ typedef enum { FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX = 6, /* Frame for transitioning to interpreted code */ FRAME_TYPE_INTERP_ENTRY = 7, - FRAME_TYPE_NUM = 8 + /* Frame marking transition to native JIT compiler */ + FRAME_TYPE_JIT_ENTRY = 8, + FRAME_TYPE_NUM = 9 } MonoStackFrameType; typedef enum { 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 4c50a08f007c8..059efeed70cea 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -1,5 +1,8 @@ { "version": "${WorkloadVersion}", + "depends-on": { + "Microsoft.NET.Workload.Emscripten": "${EmscriptenVersion}" + }, "workloads": { "microsoft-net-sdk-blazorwebassembly-aot": { "description": "Browser Runtime native performance tools", @@ -11,7 +14,7 @@ "Microsoft.NET.Runtime.Emscripten.Python", "Microsoft.NET.Runtime.Emscripten.Sdk" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "linux-x64", "osx-x64", "osx-arm64" ] }, "microsoft-net-runtime-android": { @@ -266,34 +269,5 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NET.Runtime.Emscripten.Node" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Python" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Sdk" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64" - } - } } } \ No newline at end of file diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index ab039e9a71678..4176041469bc1 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -44,9 +44,6 @@ - - - diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 64d7b9d658f94..8455ce69a39d8 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -71,7 +71,7 @@ MONO_LIBS = \ $(ICU_LIBDIR)/libicuuc.a \ $(ICU_LIBDIR)/libicui18n.a -EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DDEBUG=1 +EMCC_DEBUG_FLAGS =-g -Os -s -DDEBUG=1 EMCC_RELEASE_FLAGS=-Oz ifeq ($(NOSTRIP),) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index e1de26e4a4cde..50e491ac060f7 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -160,13 +160,14 @@ <_WasmRuntimeICallTablePath>$(_WasmIntermediateOutputPath)runtime-icall-table.h <_WasmPInvokeTablePath>$(_WasmIntermediateOutputPath)pinvoke-table.h - <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 + <_EmccAssertionLevelDefault>0 + <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(OS)' != 'Windows_NT' and '$(Configuration)' == 'Debug'">-Os <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' != 'Debug'">-Oz <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz $(_EmccOptimizationFlagDefault) - -O0 + -O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index c8186ff30d3b6..45640378ac3cc 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -178,7 +178,7 @@ DependsOnTargets="GenerateEmccPropsAndRspFiles;BuildPInvokeTable;BundleTimezones"> - -g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 + -g -Os -s -DENABLE_NETCORE=1 -DDEBUG=1 -Oz -DENABLE_NETCORE=1 "$(EMSDK_PATH)/upstream/bin/wasm-opt" --strip-dwarf "$(NativeBinDir)dotnet.wasm" -o "$(NativeBinDir)dotnet.wasm" $(ArtifactsObjDir)wasm diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index 159c01ff3f27b..c6b2f2ad28e94 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -5,7 +5,6 @@ - @@ -13,6 +12,9 @@ + + + diff --git a/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs b/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs index dfd292b755bec..acff1809714c4 100644 --- a/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs +++ b/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs @@ -6,6 +6,9 @@ internal class FloatOvfToInt { + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool BreakUpFlow() => false; + [MethodImpl(MethodImplOptions.NoInlining)] public static long FloatToLong(float f) { @@ -228,6 +231,64 @@ public static int TestValuesFloatLong() return 100; } + public static int TestValuesFloatLongVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + return 100; + } + + public static int TestValuesFloatLongImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToLong(bigf) != FloatToLongInline(100000000000000000000000000000.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(100000000000000000000000000000.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-100000000000000000000000000000.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-100000000000000000000000000000.0f)) return 504; + + bigf = 987654321001234567899876543210.0f; + if (FloatToLong(bigf) != FloatToLongInline(987654321001234567899876543210.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(987654321001234567899876543210.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-987654321001234567899876543210.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-987654321001234567899876543210.0f)) return 504; + + bigf = 254783961024896571038054632179.0f; + if (FloatToLong(bigf) != FloatToLongInline(254783961024896571038054632179.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(254783961024896571038054632179.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-254783961024896571038054632179.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-254783961024896571038054632179.0f)) return 504; + + return 100; + } + public static int TestValuesFloatInt() { float bigf = 100000000000000000000000000000.0f; @@ -251,6 +312,64 @@ public static int TestValuesFloatInt() return 100; } + public static int TestValuesFloatIntVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + return 100; + } + + public static int TestValuesFloatIntImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToInt(bigf) != FloatToIntInline(100000000000000000000000000000.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(100000000000000000000000000000.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-100000000000000000000000000000.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-100000000000000000000000000000.0f)) return 514; + + bigf = 987654321001234567899876543210.0f; + if (FloatToInt(bigf) != FloatToIntInline(987654321001234567899876543210.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(987654321001234567899876543210.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-987654321001234567899876543210.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-987654321001234567899876543210.0f)) return 514; + + bigf = 254783961024896571038054632179.0f; + if (FloatToInt(bigf) != FloatToIntInline(254783961024896571038054632179.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(254783961024896571038054632179.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-254783961024896571038054632179.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-254783961024896571038054632179.0f)) return 514; + + return 100; + } + public static int TestValuesFloatShort() { float bigf = 100000000000000000000000000000.0f; @@ -274,6 +393,64 @@ public static int TestValuesFloatShort() return 100; } + public static int TestValuesFloatShortVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + return 100; + } + + public static int TestValuesFloatShortImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToShort(bigf) != FloatToShortInline(100000000000000000000000000000.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(100000000000000000000000000000.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-100000000000000000000000000000.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-100000000000000000000000000000.0f)) return 524; + + bigf = 987654321001234567899876543210.0f; + if (FloatToShort(bigf) != FloatToShortInline(987654321001234567899876543210.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(987654321001234567899876543210.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-987654321001234567899876543210.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-987654321001234567899876543210.0f)) return 524; + + bigf = 254783961024896571038054632179.0f; + if (FloatToShort(bigf) != FloatToShortInline(254783961024896571038054632179.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(254783961024896571038054632179.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-254783961024896571038054632179.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-254783961024896571038054632179.0f)) return 524; + + return 100; + } + public static int TestValuesFloatByte() { float bigf = 100000000000000000000000000000.0f; @@ -297,21 +474,91 @@ public static int TestValuesFloatByte() return 100; } + public static int TestValuesFloatByteVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + return 100; + } + + public static int TestValuesFloatByteImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(100000000000000000000000000000.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(100000000000000000000000000000.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-100000000000000000000000000000.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-100000000000000000000000000000.0f)) return 544; + + bigf = 987654321001234567899876543210.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(987654321001234567899876543210.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(987654321001234567899876543210.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-987654321001234567899876543210.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-987654321001234567899876543210.0f)) return 544; + + bigf = 254783961024896571038054632179.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(254783961024896571038054632179.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(254783961024896571038054632179.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-254783961024896571038054632179.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-254783961024896571038054632179.0f)) return 544; + + return 100; + } + public static int TestValuesDoubleLong() { double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 204; bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 204; bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; @@ -320,6 +567,52 @@ public static int TestValuesDoubleLong() return 100; } + public static int TestValuesDoubleLongVN() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + bigd = 987654321001234567899876543210.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + bigd = 254783961024896571038054632179.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + return 100; + } + + public static int TestValuesDoubleLongImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToLong(bigd) != DoubleToLongInline(100000000000000000000000000000.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(100000000000000000000000000000.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-100000000000000000000000000000.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-100000000000000000000000000000.0)) return 604; + + bigd = 987654321001234567899876543210.0; + if (DoubleToLong(bigd) != DoubleToLongInline(987654321001234567899876543210.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(987654321001234567899876543210.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-987654321001234567899876543210.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-987654321001234567899876543210.0)) return 604; + + bigd = 254783961024896571038054632179.0; + if (DoubleToLong(bigd) != DoubleToLongInline(254783961024896571038054632179.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(254783961024896571038054632179.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-254783961024896571038054632179.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-254783961024896571038054632179.0)) return 604; + + return 100; + } + public static int TestValuesDoubleInt() { double bigd = 100000000000000000000000000000.0; @@ -343,6 +636,64 @@ public static int TestValuesDoubleInt() return 100; } + public static int TestValuesDoubleIntVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + return 100; + } + + public static int TestValuesDoubleIntImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToInt(bigd) != DoubleToIntInline(100000000000000000000000000000.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(100000000000000000000000000000.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-100000000000000000000000000000.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-100000000000000000000000000000.0)) return 614; + + bigd = 987654321001234567899876543210.0; + if (DoubleToInt(bigd) != DoubleToIntInline(987654321001234567899876543210.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(987654321001234567899876543210.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-987654321001234567899876543210.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-987654321001234567899876543210.0)) return 614; + + bigd = 254783961024896571038054632179.0; + if (DoubleToInt(bigd) != DoubleToIntInline(254783961024896571038054632179.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(254783961024896571038054632179.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-254783961024896571038054632179.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-254783961024896571038054632179.0)) return 614; + + return 100; + } + public static int TestValuesDoubleShort() { double bigd = 100000000000000000000000000000.0; @@ -366,6 +717,64 @@ public static int TestValuesDoubleShort() return 100; } + public static int TestValuesDoubleShortVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + return 100; + } + + public static int TestValuesDoubleShortImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToShort(bigd) != DoubleToShortInline(100000000000000000000000000000.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(100000000000000000000000000000.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-100000000000000000000000000000.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 624; + + bigd = 987654321001234567899876543210.0; + if (DoubleToShort(bigd) != DoubleToShortInline(987654321001234567899876543210.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(987654321001234567899876543210.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-987654321001234567899876543210.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-987654321001234567899876543210.0)) return 624; + + bigd = 254783961024896571038054632179.0; + if (DoubleToShort(bigd) != DoubleToShortInline(254783961024896571038054632179.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(254783961024896571038054632179.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-254783961024896571038054632179.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-254783961024896571038054632179.0)) return 624; + + return 100; + } + public static int TestValuesDoubleByte() { double bigd = 100000000000000000000000000000.0; @@ -389,17 +798,91 @@ public static int TestValuesDoubleByte() return 100; } + public static int TestValuesDoubleByteVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + return 100; + } + + public static int TestValuesDoubleByteImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(100000000000000000000000000000.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(100000000000000000000000000000.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-100000000000000000000000000000.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-100000000000000000000000000000.0)) return 644; + + bigd = 987654321001234567899876543210.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(987654321001234567899876543210.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(987654321001234567899876543210.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-987654321001234567899876543210.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-987654321001234567899876543210.0)) return 644; + + bigd = 254783961024896571038054632179.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(987654321001234567899876543210.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(987654321001234567899876543210.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-987654321001234567899876543210.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-987654321001234567899876543210.0)) return 644; + + return 100; + } + public static int TestValues() { int res = TestValuesFloatLong(); if (res != 100) return res; + res = TestValuesFloatLongVN(); if (res != 100) return res; + res = TestValuesFloatLongImport(); if (res != 100) return res; res = TestValuesFloatInt(); if (res != 100) return res; + res = TestValuesFloatIntVN(); if (res != 100) return res; + res = TestValuesFloatIntImport(); if (res != 100) return res; res = TestValuesFloatShort(); if (res != 100) return res; + res = TestValuesFloatShortImport(); if (res != 100) return res; + res = TestValuesFloatShortVN(); if (res != 100) return res; res = TestValuesFloatByte(); if (res != 100) return res; + res = TestValuesFloatByteImport(); if (res != 100) return res; + res = TestValuesFloatByteVN(); if (res != 100) return res; res = TestValuesDoubleLong(); if (res != 100) return res; + res = TestValuesDoubleLongVN(); if (res != 100) return res; + res = TestValuesDoubleLongImport(); if (res != 100) return res; res = TestValuesDoubleInt(); if (res != 100) return res; + res = TestValuesDoubleIntVN(); if (res != 100) return res; + res = TestValuesDoubleIntImport(); if (res != 100) return res; res = TestValuesDoubleShort(); if (res != 100) return res; + res = TestValuesDoubleShortVN(); if (res != 100) return res; + res = TestValuesDoubleShortImport(); if (res != 100) return res; res = TestValuesDoubleByte(); if (res != 100) return res; + res = TestValuesDoubleByteVN(); if (res != 100) return res; + res = TestValuesDoubleByteImport(); if (res != 100) return res; return res; } diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.targets b/src/tests/Loader/binding/tracing/BinderTracingTest.targets index 426a5556093f1..67b0d2d3a20fa 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.targets +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.targets @@ -38,15 +38,16 @@ fr-FR + $(OutDir)/DependentAssemblies - + - - - + + + diff --git a/src/tests/build.proj b/src/tests/build.proj index d1ce4ab95ba51..e06e245e96548 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -43,7 +43,7 @@ - <_ConfigurationProperties>/p:TargetOS=$(TargetOS) /p:TargetArchitecture=$(TargetArchitecture) /p:Configuration=$(Configuration) + <_ConfigurationProperties>/p:TargetOS=$(TargetOS) /p:TargetArchitecture=$(TargetArchitecture) /p:Configuration=$(Configuration) /p:CrossBuild=$(CrossBuild) "$(DotNetTool)" restore $(RestoreProj) $(PackageVersionArg) /p:SetTFMForRestore=true $(_ConfigurationProperties) "$(DotNetTool)" restore -r $(__DistroRid) $(RestoreProj) $(PackageVersionArg) /p:SetTFMForRestore=true $(_ConfigurationProperties) diff --git a/src/tests/build.sh b/src/tests/build.sh index da6fa03e1cfcf..e5575f69f98ff 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -573,6 +573,10 @@ if [[ "${__BuildArch}" != "${__HostArch}" ]]; then __CrossBuild=1 fi +if [[ "$__CrossBuild" == 1 ]]; then + __UnprocessedBuildArgs+=("/p:CrossBuild=true") +fi + # Set dependent variables __LogsDir="$__RootBinDir/log" __MsbuildDebugLogsDir="$__LogsDir/MsbuildDebugLogs" diff --git a/src/tests/issues.targets b/src/tests/issues.targets index f94900a742146..9aa21e9c19cf2 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1026,6 +1026,18 @@ Mono does not define out of range fp to int conversions + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + https://github.com/dotnet/runtime/issues/48190 @@ -2203,12 +2215,6 @@ https://github.com/dotnet/runtime/issues/54376 - - https://github.com/dotnet/runtime/issues/54374e - - - https://github.com/dotnet/runtime/issues/54374 -