Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Safar <marek.safar@gmail.com>2018-03-02 12:41:04 +0300
committerMarek Safar <marek.safar@gmail.com>2018-03-02 12:41:04 +0300
commit751b91ba5ed72c4506051d3d5bfcdd226020179b (patch)
tree9ae1bd139b32d3c7d190d77f81cdfacd297edbd1
parent71513aa41e97ba935f183d9b3eb275b1e6a5659a (diff)
parent5ae871ca2298c8caaf8966204360f5b67c5404bb (diff)
Merge remote-tracking branch 'upstream/master'
-rw-r--r--BuildToolsVersion.txt2
-rw-r--r--Documentation/how-to-build-WebAssembly.md29
-rw-r--r--Documentation/how-to-run-tests.md5
-rw-r--r--Documentation/prerequisites-for-building.md19
-rw-r--r--DotnetCLIVersion.txt2
-rw-r--r--Packaging.props3
-rw-r--r--README.md3
-rw-r--r--buildscripts/build-managed.cmd5
-rwxr-xr-xbuildscripts/build-managed.sh10
-rw-r--r--buildscripts/buildvars-setup.cmd12
-rwxr-xr-xbuildscripts/buildvars-setup.sh102
-rw-r--r--cross/arm/toolchain.cmake8
-rw-r--r--cross/arm64/toolchain.cmake8
-rw-r--r--cross/armel/toolchain.cmake8
-rw-r--r--cross/x86/toolchain.cmake8
-rw-r--r--dependencies.props8
-rwxr-xr-xinit-tools.sh4
-rw-r--r--samples/MonoGame/NeonShooter.csproj92
-rw-r--r--samples/MonoGame/Platformer2D.csproj71
-rw-r--r--samples/MonoGame/README.md94
-rw-r--r--samples/MonoGame/nuget.config9
-rw-r--r--samples/MonoGame/rd.xml12
-rw-r--r--samples/WebApi/README.md10
-rw-r--r--samples/WebApi/SampleWebApi.csproj5
-rw-r--r--src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets3
-rw-r--r--src/BuildIntegration/Microsoft.NETCore.Native.Unix.props32
-rw-r--r--src/BuildIntegration/Microsoft.NETCore.Native.Windows.props50
-rw-r--r--src/BuildIntegration/Microsoft.NETCore.Native.targets14
-rw-r--r--src/BuildIntegration/findvcvarsall.bat30
-rw-r--r--src/Common/src/Internal/NativeFormat/NativeFormat.cs2
-rw-r--r--src/Common/src/Internal/NativeFormat/NativeFormatWriter.cs2
-rw-r--r--src/Common/src/Internal/Text/Utf8StringBuilder.cs50
-rw-r--r--src/Common/src/Interop/Windows/mincore/Interop.MUI.cs24
-rw-r--r--src/Common/src/Interop/Windows/mincore/Interop.TimeZone.Win32.cs95
-rw-r--r--src/Common/src/Interop/Windows/mincore/Interop.TimeZone.cs1
-rw-r--r--src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs82
-rw-r--r--src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs1
-rw-r--r--src/Common/src/TypeSystem/IL/DelegateInfo.cs5
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs2
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs34
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs9
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs12
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs20
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs145
-rw-r--r--src/Common/src/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs243
-rw-r--r--src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs7
-rw-r--r--src/Common/src/TypeSystem/Interop/IL/Marshaller.cs52
-rw-r--r--src/Common/src/TypeSystem/RuntimeDetermined/DefType.RuntimeDetermined.cs6
-rw-r--r--src/Common/src/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs7
-rw-r--r--src/Common/test-runtime/XUnit.Runtime.depproj2
-rw-r--r--src/Framework/Framework-native.depproj2
-rw-r--r--src/Framework/Framework-uapaot.depproj2
-rw-r--r--src/Framework/Framework.depproj2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs41
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs21
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs6
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs17
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs32
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs3
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs10
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs33
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs73
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs3
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs3
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs6
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs12
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs1
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs7
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs7
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs5
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs21
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs68
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs11
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs120
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs69
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs7
-rw-r--r--src/ILCompiler.Compiler/src/Logger.cs5
-rw-r--r--src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs10
-rw-r--r--src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs8
-rw-r--r--src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj9
-rw-r--r--src/ILCompiler.TypeSystem/tests/UniversalGenericFieldLayoutTests.cs2
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs33
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs744
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs1
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs4
-rw-r--r--src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs1
-rw-r--r--src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj20
-rw-r--r--src/ILCompiler.WebAssembly/src/libLLVMdep.depproj21
-rw-r--r--src/ILCompiler/ILCompiler.sln116
-rw-r--r--src/ILCompiler/ObjectWriter/ObjectWriter.depproj4
-rw-r--r--src/ILCompiler/RyuJIT/RyuJIT.depproj3
-rw-r--r--src/ILCompiler/repro/repro.csproj1
-rw-r--r--src/ILCompiler/reproNative/reproNative.vcxproj4
-rw-r--r--src/ILVerification/README.md3
-rw-r--r--src/ILVerification/StrongNameKeys/ILVerify.snkbin0 -> 596 bytes
-rw-r--r--src/ILVerification/src/AccessVerificationHelpers.cs (renamed from src/ILVerify/src/AccessVerificationHelpers.cs)2
-rw-r--r--src/ILVerification/src/AssemblyInfo.cs4
-rw-r--r--src/ILVerification/src/ILImporter.StackValue.cs (renamed from src/ILVerify/src/ILImporter.StackValue.cs)0
-rw-r--r--src/ILVerification/src/ILImporter.Verify.cs (renamed from src/ILVerify/src/ILImporter.Verify.cs)10
-rw-r--r--src/ILVerification/src/ILVerification.csproj302
-rw-r--r--src/ILVerification/src/ILVerifyTypeSystemContext.cs88
-rw-r--r--src/ILVerification/src/IResolver.cs47
-rw-r--r--src/ILVerification/src/InstantiatedGenericParameter.cs (renamed from src/ILVerify/src/InstantiatedGenericParameter.cs)0
-rw-r--r--src/ILVerification/src/Resources/Strings.resx (renamed from src/ILVerify/src/Resources/Strings.resx)0
-rw-r--r--src/ILVerification/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs (renamed from src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs)0
-rw-r--r--src/ILVerification/src/TypeSystemHelpers.cs (renamed from src/ILVerify/src/TypeSystemHelpers.cs)0
-rw-r--r--src/ILVerification/src/VerificationResult.cs24
-rw-r--r--src/ILVerification/src/Verifier.cs234
-rw-r--r--src/ILVerification/src/VerifierError.cs (renamed from src/ILVerify/src/VerifierError.cs)9
-rw-r--r--src/ILVerification/tests/ILMethodTester.cs (renamed from src/ILVerify/tests/ILMethodTester.cs)47
-rw-r--r--src/ILVerification/tests/ILTests/AccessTests.il (renamed from src/ILVerify/tests/ILTests/AccessTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/AccessTestsExtern.il (renamed from src/ILVerify/tests/ILTests/AccessTestsExtern.il)0
-rw-r--r--src/ILVerification/tests/ILTests/AccessTestsFriend.il (renamed from src/ILVerify/tests/ILTests/AccessTestsFriend.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ArrayTests.il (renamed from src/ILVerify/tests/ILTests/ArrayTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/BasicArithmeticTests.il (renamed from src/ILVerify/tests/ILTests/BasicArithmeticTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/BranchingTests.il (renamed from src/ILVerify/tests/ILTests/BranchingTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/CallTests.il (renamed from src/ILVerify/tests/ILTests/CallTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/CastingTests.il (renamed from src/ILVerify/tests/ILTests/CastingTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ComparisonTests.il (renamed from src/ILVerify/tests/ILTests/ComparisonTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/DelegateTests.il (renamed from src/ILVerify/tests/ILTests/DelegateTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ExceptionRegionTests.il (renamed from src/ILVerify/tests/ILTests/ExceptionRegionTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/FieldTests.il (renamed from src/ILVerify/tests/ILTests/FieldTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/FtnTests.il (renamed from src/ILVerify/tests/ILTests/FtnTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/LoadStoreIndirectTests.il (renamed from src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/NewobjTests.il (renamed from src/ILVerify/tests/ILTests/NewobjTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/PrefixTests.il (renamed from src/ILVerify/tests/ILTests/PrefixTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ReturnTests.il (renamed from src/ILVerify/tests/ILTests/ReturnTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ShiftTests.il (renamed from src/ILVerify/tests/ILTests/ShiftTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/SwitchTests.il (renamed from src/ILVerify/tests/ILTests/SwitchTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ThisStateTests.il (renamed from src/ILVerify/tests/ILTests/ThisStateTests.il)0
-rw-r--r--src/ILVerification/tests/ILTests/ValueTypeTests.il (renamed from src/ILVerify/tests/ILTests/ValueTypeTests.il)0
-rw-r--r--src/ILVerification/tests/ILVerification.Tests.csproj39
-rw-r--r--src/ILVerification/tests/TestDataLoader.cs (renamed from src/ILVerify/tests/TestDataLoader.cs)57
-rw-r--r--src/ILVerify/ILVerify.sln19
-rw-r--r--src/ILVerify/README.md28
-rw-r--r--src/ILVerify/netcoreapp/ILVerify.cs9
-rw-r--r--src/ILVerify/netcoreapp/ILVerify.csproj14
-rw-r--r--src/ILVerify/src/AssemblyInfo.cs2
-rw-r--r--src/ILVerify/src/ILVerify.csproj309
-rw-r--r--src/ILVerify/src/ILVerify.runtimeconfig.json7
-rw-r--r--src/ILVerify/src/Program.cs284
-rw-r--r--src/ILVerify/src/SimpleTypeSystemContext.cs134
-rw-r--r--src/ILVerify/tests/ILVerify.Tests.csproj27
-rw-r--r--src/JitInterface/src/CorInfoHelpFunc.cs1
-rw-r--r--src/JitInterface/src/CorInfoImpl.Intrinsics.cs38
-rw-r--r--src/JitInterface/src/CorInfoImpl.cs10
-rw-r--r--src/JitInterface/src/CorInfoTypes.cs2
-rw-r--r--src/JitInterface/src/ThunkGenerator/corinfo.h12
-rw-r--r--src/JitInterface/src/ThunkGenerator/corjitflags.h4
-rw-r--r--src/Native/Bootstrap/common.h1
-rw-r--r--src/Native/Bootstrap/main.cpp10
-rw-r--r--src/Native/ObjWriter/llvm.patch118
-rw-r--r--src/Native/ObjWriter/llvmCap/CMakeLists.txt27
-rw-r--r--src/Native/ObjWriter/objwriter.cpp59
-rw-r--r--src/Native/ObjWriter/objwriter.exports6
-rw-r--r--src/Native/ObjWriter/objwriter.h84
-rw-r--r--src/Native/Runtime/AsmOffsets.h7
-rw-r--r--src/Native/Runtime/AsmOffsetsVerify.cpp4
-rw-r--r--src/Native/Runtime/CachedInterfaceDispatch.cpp2
-rw-r--r--src/Native/Runtime/DebugFuncEval.cpp35
-rw-r--r--src/Native/Runtime/DebugFuncEval.h14
-rw-r--r--src/Native/Runtime/EHHelpers.cpp52
-rw-r--r--src/Native/Runtime/ICodeManager.h47
-rw-r--r--src/Native/Runtime/MiscHelpers.cpp16
-rw-r--r--src/Native/Runtime/Portable/CMakeLists.txt4
-rw-r--r--src/Native/Runtime/RHCodeMan.cpp110
-rw-r--r--src/Native/Runtime/RhConfig.cpp8
-rw-r--r--src/Native/Runtime/RhConfig.h4
-rw-r--r--src/Native/Runtime/RuntimeInstance.cpp57
-rw-r--r--src/Native/Runtime/RuntimeInstance.h5
-rw-r--r--src/Native/Runtime/StackFrameIterator.cpp16
-rw-r--r--src/Native/Runtime/amd64/GcProbe.asm42
-rw-r--r--src/Native/Runtime/arm64/ExceptionHandling.asm70
-rw-r--r--src/Native/Runtime/arm64/GcProbe.asm108
-rw-r--r--src/Native/Runtime/arm64/PInvoke.asm7
-rw-r--r--src/Native/Runtime/arm64/UniversalTransition.asm2
-rw-r--r--src/Native/Runtime/coreclr/gcinfodecoder.cpp8
-rw-r--r--src/Native/Runtime/gcdump.cpp470
-rw-r--r--src/Native/Runtime/inc/WellKnownMethodList.h2
-rw-r--r--src/Native/Runtime/inc/WellKnownMethods.h4
-rw-r--r--src/Native/Runtime/inc/gcinfo.h113
-rw-r--r--src/Native/Runtime/inc/rhbinder.h57
-rw-r--r--src/Native/Runtime/module.cpp8
-rw-r--r--src/Native/Runtime/stressLog.cpp2
-rw-r--r--src/Native/Runtime/thread.cpp70
-rw-r--r--src/Native/Runtime/thread.h25
-rw-r--r--src/Native/Runtime/threadstore.cpp6
-rw-r--r--src/Native/Runtime/unix/PalRedhawkUnix.cpp7
-rw-r--r--src/Native/Runtime/unix/UnixContext.cpp5
-rw-r--r--src/Native/Runtime/unix/UnwindHelpers.cpp27
-rw-r--r--src/Native/Runtime/windows/CoffNativeCodeManager.cpp2
-rw-r--r--src/Native/Runtime/windows/PalRedhawkMinWin.cpp17
-rw-r--r--src/Native/System.Private.CoreLib.Native/pal_threading.cpp2
-rw-r--r--src/Native/gc/gc.cpp62
-rw-r--r--src/Native/gc/handletable.cpp2
-rw-r--r--src/Native/gc/handletablecore.cpp18
-rw-r--r--src/Native/gc/handletablescan.cpp14
-rw-r--r--src/Native/gc/objecthandle.cpp4
-rw-r--r--src/Native/gc/unix/gcenv.unix.cpp4
-rwxr-xr-xsrc/Native/gen-buildsys-clang.sh1
-rw-r--r--src/Native/jitinterface/jitwrapper.cpp10
-rw-r--r--src/Native/libunwind/src/Unwind-EHABI.cpp2
-rw-r--r--src/Native/libunwind/src/Unwind_AppleExtras.cpp2
-rw-r--r--src/Native/probe-win.ps118
-rw-r--r--src/Runtime.Base/src/Runtime.Base.csproj2
-rw-r--r--src/Runtime.Base/src/System/Diagnostics/Eval.cs16
-rw-r--r--src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs2
-rw-r--r--src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs2
-rw-r--r--src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs65
-rw-r--r--src/Runtime.Base/src/System/Runtime/InternalCalls.cs20
-rw-r--r--src/Runtime.Base/src/System/Runtime/ThunkPool.cs4
-rw-r--r--src/System.Private.CoreLib/shared/Internal/IO/File.Unix.cs25
-rw-r--r--src/System.Private.CoreLib/shared/Internal/IO/File.Windows.cs77
-rw-r--r--src/System.Private.CoreLib/shared/Internal/IO/File.cs (renamed from src/System.Private.CoreLib/src/System/IO/InternalFile.cs)36
-rw-r--r--src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs40
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/Interop.Libraries.cs2
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs12
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Casing.cs8
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs36
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs4
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Idna.cs6
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs16
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs6
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs2
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs4
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs8
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs2
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadDir.cs102
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.Stat.cs10
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs1
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs4
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs13
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs13
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs4
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MAX_PATH.cs (renamed from src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs)9
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MUI.cs18
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.Registry.cs31
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.cs94
-rw-r--r--src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeDirectoryHandle.Unix.cs26
-rw-r--r--src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs11
-rw-r--r--src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs (renamed from src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs)10
-rw-r--r--src/System.Private.CoreLib/shared/README.md4
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems73
-rw-r--r--src/System.Private.CoreLib/shared/System/AggregateException.cs6
-rw-r--r--src/System.Private.CoreLib/shared/System/ArraySegment.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Boolean.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs11
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs52
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs54
-rw-r--r--src/System.Private.CoreLib/shared/System/Char.cs10
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/Comparer.cs77
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs320
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs6
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/Generic/ValueListBuilder.cs76
-rw-r--r--src/System.Private.CoreLib/shared/System/Convert.cs18
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Debug.Unix.cs18
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs39
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs1273
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs10
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs13
-rw-r--r--src/System.Private.CoreLib/shared/System/Double.cs21
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs88
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs10
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs13
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs78
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs20
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs (renamed from src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs)81
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CultureData.cs (renamed from src/System.Private.CoreLib/src/System/Globalization/CultureData.cs)698
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs3
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs21
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs23
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeStyles.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/GlobalizationExtensions.cs32
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Unix.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/InternalGlobalizationHelper.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Unix.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs55
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs38
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs42
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs3
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs43
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStream.cs25
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs20
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs116
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs207
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/Path.cs599
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs414
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs34
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.StringBuffer.cs93
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs209
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathInternal.cs71
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/StreamReader.cs1422
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs986
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/TextReader.cs412
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/TextWriter.cs870
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs10
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Math.cs104
-rw-r--r--src/System.Private.CoreLib/shared/System/Memory.cs106
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryDebugView.cs6
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs516
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.cs1170
-rw-r--r--src/System.Private.CoreLib/shared/System/Number.Formatting.cs9
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.cs142
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.tt60
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/GenerationConfig.ttinclude147
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/Register.cs172
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/Register.tt46
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/Vector.cs5503
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/Vector.tt1808
-rw-r--r--src/System.Private.CoreLib/shared/System/Numerics/Vector_Operations.cs865
-rw-r--r--src/System.Private.CoreLib/shared/System/OverflowException.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs39
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs270
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs251
-rw-r--r--src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs54
-rw-r--r--src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs1
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs112
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs222
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs3
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs192
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs (renamed from src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs)58
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs170
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs108
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StringBuffer.cs301
-rw-r--r--src/System.Private.CoreLib/shared/System/Security/SecureString.cs23
-rw-r--r--src/System.Private.CoreLib/shared/System/Single.cs18
-rw-r--r--src/System.Private.CoreLib/shared/System/Span.Fast.cs350
-rw-r--r--src/System.Private.CoreLib/shared/System/Span.cs333
-rw-r--r--src/System.Private.CoreLib/shared/System/SpanHelpers.BinarySearch.cs83
-rw-r--r--src/System.Private.CoreLib/shared/System/SpanHelpers.Byte.cs1104
-rw-r--r--src/System.Private.CoreLib/shared/System/SpanHelpers.T.cs683
-rw-r--r--src/System.Private.CoreLib/shared/System/SpanHelpers.cs (renamed from src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs)241
-rw-r--r--src/System.Private.CoreLib/shared/System/String.Manipulation.cs (renamed from src/System.Private.CoreLib/src/System/String.Manipulation.cs)692
-rw-r--r--src/System.Private.CoreLib/shared/System/String.Searching.cs212
-rw-r--r--src/System.Private.CoreLib/shared/System/StringComparer.cs71
-rw-r--r--src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs54
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs147
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs60
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs294
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/IValueTaskSource.cs82
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs783
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.AdjustmentRule.cs248
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs625
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.TransitionTime.cs155
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.Unix.cs1560
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.Win32.cs999
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeZoneInfo.cs1993
-rw-r--r--src/System.Private.CoreLib/shared/System/Version.cs4
-rw-r--r--src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs2
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs3
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs9
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs5
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs13
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs2
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs15
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs1
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs3
-rw-r--r--src/System.Private.CoreLib/src/Interop/Interop.manual.cs1
-rw-r--r--src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData4
-rw-r--r--src/System.Private.CoreLib/src/Resources/Strings.resx67
-rw-r--r--src/System.Private.CoreLib/src/System.Private.CoreLib.csproj66
-rw-r--r--src/System.Private.CoreLib/src/System/AppContext.Windows.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Array.CoreRT.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Array.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Buffer.cs254
-rw-r--r--src/System.Private.CoreLib/src/System/Collections/LowLevelComparer.cs39
-rw-r--r--src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs1009
-rw-r--r--src/System.Private.CoreLib/src/System/Decimal.cs137
-rw-r--r--src/System.Private.CoreLib/src/System/Delegate.cs18
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/LowLevelDebugFuncEval.cs39
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs21
-rw-r--r--src/System.Private.CoreLib/src/System/Enum.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Environment.Win32.cs21
-rw-r--r--src/System.Private.CoreLib/src/System/Environment.cs13
-rw-r--r--src/System.Private.CoreLib/src/System/GC.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs597
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs94
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs5
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs31
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs114
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs136
-rw-r--r--src/System.Private.CoreLib/src/System/IO/Stream.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/InvokeUtils.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/Math.CoreRT.cs30
-rw-r--r--src/System.Private.CoreLib/src/System/MathF.CoreRT.cs105
-rw-r--r--src/System.Private.CoreLib/src/System/MissingFieldException.cs4
-rw-r--r--src/System.Private.CoreLib/src/System/MulticastDelegate.cs16
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/CustomAttributeExtensions.cs13
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeImplementedCustomAttributeData.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs26
-rw-r--r--src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs42
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs15
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs4
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs17
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs26
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs45
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs222
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs9
-rw-r--r--src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs6
-rw-r--r--src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs6
-rw-r--r--src/System.Private.CoreLib/src/System/String.Comparison.cs103
-rw-r--r--src/System.Private.CoreLib/src/System/String.CoreRT.cs85
-rw-r--r--src/System.Private.CoreLib/src/System/String.cs64
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Condition.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Lock.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/LockHolder.cs1
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/PinnableBufferCache.cs4
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Semaphore.Windows.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Semaphore.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/SpinLock.cs8
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs215
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/ThrowHelper.cs40
-rw-r--r--src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs933
-rw-r--r--src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs1161
-rw-r--r--src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs679
-rw-r--r--src/System.Private.CoreLib/src/System/TimeZoneInfo.cs3870
-rw-r--r--src/System.Private.CoreLib/src/System/Type.CoreRT.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/ValueType.cs163
-rw-r--r--src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs30
-rw-r--r--src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs17
-rw-r--r--src/System.Private.Interop/src/Interop/Interop.WinRT.cs2
-rw-r--r--src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Unix.cs81
-rw-r--r--src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Windows.cs156
-rw-r--r--src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs409
-rw-r--r--src/System.Private.Interop/src/Resources/Strings.resx6
-rw-r--r--src/System.Private.Interop/src/Shared/ComCallableObject.cs2
-rw-r--r--src/System.Private.Interop/src/Shared/Marshal.cs3
-rw-r--r--src/System.Private.Interop/src/Shared/McgMarshal.cs11
-rw-r--r--src/System.Private.Interop/src/Shared/McgModuleManager.cs4
-rw-r--r--src/System.Private.Interop/src/Shared/McgPInvokeData.cs (renamed from src/Common/src/System/Runtime/InteropServices/McgPInvokeData.cs)21
-rw-r--r--src/System.Private.Interop/src/Shared/McgTypeHelpers.cs2
-rw-r--r--src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj21
-rw-r--r--src/System.Private.Interop/src/System.Private.Interop.Mono.csproj23
-rw-r--r--src/System.Private.Interop/src/System.Private.Interop.Shared.projitems3
-rw-r--r--src/System.Private.Interop/src/System.Private.Interop.csproj4
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/bindptr.cs3
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/dispparams.cs2
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/excepinfo.cs2
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/formatetc.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/funcdesc.cs2
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/idldesc.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/paramdesc.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/stgmedium.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typeattr.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typedesc.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/vardesc.cs1
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs28
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs43
-rw-r--r--src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs76
-rw-r--r--src/System.Private.Interop/src/TypeForwarders.cs5
-rw-r--r--src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs2
-rw-r--r--src/System.Private.Jit/src/System.Private.Jit.csproj4
-rw-r--r--src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs10
-rw-r--r--src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs8
-rw-r--r--src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs10
-rw-r--r--src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs12
-rw-r--r--src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs5
-rw-r--r--src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs2
-rw-r--r--src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs2
-rw-r--r--src/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs11
-rw-r--r--src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs7
-rw-r--r--src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs1
-rw-r--r--src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj13
-rw-r--r--src/Test.CoreLib/src/Test.CoreLib.csproj2
-rw-r--r--src/dirs.proj8
-rw-r--r--tests/CoreCLR.issues.targets354
-rw-r--r--tests/CoreCLR/build-and-run-test.cmd2
-rw-r--r--tests/CoreCLRTestsURL.txt2
-rw-r--r--tests/Top200.CoreCLR.issues.targets321
-rw-r--r--tests/Top200.unix.txt1
-rwxr-xr-xtests/runtest.sh53
-rw-r--r--tests/src/Simple/HelloWasm/HelloWasm.csproj8
-rw-r--r--tests/src/Simple/HelloWasm/Program.cs209
-rw-r--r--tests/src/Simple/HelloWasm/no_unix1
-rw-r--r--tests/src/Simple/PInvoke/PInvoke.csproj2
-rw-r--r--tests/src/Simple/Reflection/Reflection.cs20
-rwxr-xr-xtests/testenv.sh65
544 files changed, 37639 insertions, 14859 deletions
diff --git a/BuildToolsVersion.txt b/BuildToolsVersion.txt
index 8cc3da5de..4edf3134b 100644
--- a/BuildToolsVersion.txt
+++ b/BuildToolsVersion.txt
@@ -1 +1 @@
-2.1.0-prerelease-02403-01
+2.1.0-preview2-02521-03
diff --git a/Documentation/how-to-build-WebAssembly.md b/Documentation/how-to-build-WebAssembly.md
index 72fb034b2..84e6ed6df 100644
--- a/Documentation/how-to-build-WebAssembly.md
+++ b/Documentation/how-to-build-WebAssembly.md
@@ -1,5 +1,6 @@
# Build WebAssembly #
-Currently, building WebAssembly is only possible on Windows.
+
+## Build WebAssembly on Windows ##
1. Install Emscripten by following the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
2. Follow the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#updating-the-sdk) to update Emscripten to the latest version.
@@ -7,15 +8,33 @@ Currently, building WebAssembly is only possible on Windows.
4. Build the WebAssembly runtime by running ```build.cmd wasm``` from the repo root.
5. Run the WebAssembly "Hello World" test by running ```C:\corert\tests\runtest.cmd wasm```.
-To debug compiling WebAssembly:
+## Build WebAssembly on OSX ##
+
+1. Install Emscripten by following the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
+2. Follow the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#updating-the-sdk) to update Emscripten to the latest version.
+3. Get CoreRT set up by installing [Prerequisites](prerequisites-for-building.md).
+4. Build the WebAssembly runtime by running ```./build.sh wasm``` from the repo root.
+5. Run the WebAssembly "Hello World" test by opening it in [Firefox](https://www.getfirefox.com).
+
+## Build WebAssembly on Ubuntu 16.04.3 ##
+
+1. Get CoreRT set up by installing [Prerequisites](prerequisites-for-building.md), except install ```libicu55``` for Ubuntu 16 instead of ```libicu52```.
+2. Install Emscripten by following the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
+3. Follow the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#updating-the-sdk) to update Emscripten to the latest version.
+4. Build the WebAssembly runtime by running ```./build.sh wasm``` from the repo root.
+5. Run the WebAssembly "Hello World" test by opening it in [Firefox](https://www.getfirefox.com).
+
+
+# How to debug the IL->WebAssembly compilation #
+This is Windows only for now.
1. Set the ILCompiler startup command line to ```@C:\corert\tests\src\Simple\HelloWasm\obj\Debug\wasm\native\HelloWasm.ilc.rsp```. That will generate HelloWasm.bc, an LLVM bitcode file.
2. To compile that to WebAssembly, run ```emcc HelloWasm.bc -s ALLOW_MEMORY_GROWTH=1 C:\corert\bin\WebAssembly.wasm.Debug\sdk\libPortableRuntime.bc C:\corert\bin\WebAssembly.wasm.Debug\sdk\libbootstrappercpp.bc -s WASM=1 -o HelloWasm.html``` (if emcc isn't on your path, you'll need to launch an Emscripten command prompt to do this). That will generate a .wasm file with your code as well as html and js files to run it.
-To run a WebAssembly application
-1. Ensure you have Edge 41 or above or [Firefox](https://www.getfirefox.com).
+# How to run a WebAssembly application #
+1. Ensure you have Edge 41 (Windows only) or above or [Firefox](https://www.getfirefox.com).
2. Open the generated html file in Edge or Firefox and look at the on-screen console for output.
-Useful tips:
+# Useful tips #
* To manually make ILC compile to WebAssembly, add ```--wasm``` to the command line.
* Add ```-g3``` to the emcc command line to generate more debuggable output and a .wast file with the text form of the WebAssembly.
* Omit ```-s WASM=1``` from the emcc command line to generate asm.js. Browser debuggers currently work better with asm.js and it's often a bit more readable than wast.
diff --git a/Documentation/how-to-run-tests.md b/Documentation/how-to-run-tests.md
index b38b1df4f..9e43d1191 100644
--- a/Documentation/how-to-run-tests.md
+++ b/Documentation/how-to-run-tests.md
@@ -28,8 +28,9 @@ tests\runtest.sh
You should see the below message when you build CoreRT or run the local tests manually, otherwise something is broken.
```
-JIT - TOTAL: 7 PASSED: 7
+JIT - TOTAL: 12 PASSED: 12
CPP - TOTAL: 2 PASSED: 2
+WASM - TOTAL: 1 PASSED: 1
```
## External Tests
@@ -55,7 +56,7 @@ tests\runtest.cmd /coreclr Top200|All|KnownGood
On Linux / macOS:
-**TBD**
+tests/runtest.sh -coreclr Top200|All|KnownGood
### Suppress Windows Error Reporting Dialogs
It's advisable to use some sort of a dialog killer tool if you see test regressions as many tests fail with pop-ups for Windows Error Reporting. However, the following regedit scripts have also proven to be useful to mask these pop-ups.
diff --git a/Documentation/prerequisites-for-building.md b/Documentation/prerequisites-for-building.md
index b8360e428..368e8f4cd 100644
--- a/Documentation/prerequisites-for-building.md
+++ b/Documentation/prerequisites-for-building.md
@@ -20,7 +20,7 @@ sudo apt-get update
```
```sh
-sudo apt-get install cmake clang-3.9 libicu52 libunwind8 uuid-dev
+sudo apt-get install cmake clang-3.9 libicu52 libunwind8 uuid-dev libcurl4-openssl-dev zlib1g-dev
```
# macOS (10.12+)
@@ -28,6 +28,23 @@ sudo apt-get install cmake clang-3.9 libicu52 libunwind8 uuid-dev
1. Install [Command Line Tools for XCode 8](https://developer.apple.com/xcode/download/) or higher.
2. Install [CMake](https://cmake.org/download/) 3.8.0 or later. Launch `/Applications/CMake.app/Contents/MacOS/CMake` GUI. Goto "OSX App Menu -> Tools -> Install For Command Line Use" and follow the steps.
+# openSUSE Leap 42.3
+
+First install llvm-3.9. This is a bit cumbersome because the LLVM that comes from the `zypper` feeds is too old.
+
+```sh
+wget http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-opensuse13.2.tar.xz
+tar xf clang+llvm-3.9.0-x86_64-opensuse13.2.tar.xz
+cd clang+llvm-3.9.0-x86_64-opensuse13.2
+sudo cp -R * /usr/local/
+```
+
+Next install the rest of the dependencies:
+
+```sh
+sudo zypper install cmake libuuid-devel icu libcurl-devel zlib-devel
+```
+
# Bash on Ubuntu on Windows (Windows 10 Creators Update or later)
Make sure you run with Ubuntu 16.04 Xenial userland (this is the default after Windows 10 Creators Update, but if you enabled the "Bash on Ubuntu on Windows" feature before the Creators Update, you need to [upgrade manually](https://blogs.msdn.microsoft.com/commandline/2017/04/11/windows-10-creators-update-whats-new-in-bashwsl-windows-console/)). Running `lsb_release -a` will give you the version.
diff --git a/DotnetCLIVersion.txt b/DotnetCLIVersion.txt
index 227cea215..eca07e4c1 100644
--- a/DotnetCLIVersion.txt
+++ b/DotnetCLIVersion.txt
@@ -1 +1 @@
-2.0.0
+2.1.2
diff --git a/Packaging.props b/Packaging.props
index 88abaa019..339171f66 100644
--- a/Packaging.props
+++ b/Packaging.props
@@ -8,8 +8,7 @@
<PackageThirdPartyNoticesFile>$(ProjectDir)THIRD-PARTY-NOTICES.TXT</PackageThirdPartyNoticesFile>
<ReleaseNotes>TODO</ReleaseNotes>
<ProjectUrl>https://dot.net</ProjectUrl>
- <!-- Add a condition for this when we are able to run on .NET Core -->
- <PackagingTaskDir>$(ToolsDir)net46/</PackagingTaskDir>
+ <PackagingTaskDir>$(BuildToolsTaskDir)</PackagingTaskDir>
<!-- defined in buildtools packaging.targets, but we need this before targets are imported -->
<PackagePlatform Condition="'$(PackagePlatform)' == ''">$(Platform)</PackagePlatform>
<PackagePlatform Condition="'$(PackagePlatform)' == 'amd64'">x64</PackagePlatform>
diff --git a/README.md b/README.md
index 12181ef2e..e5bc3366f 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,7 @@ This repo contains the .NET Core runtime optimized for AOT compilation
## Platform Support
This is a work in progress. The current state of platform support:
-- Windows x64 w/ RyuJIT codegen: Simple ASP.NET apps [compile and run](https://github.com/dotnet/corert/tree/master/samples/WebApi)
-- MacOS and Linux x64 w/ RyuJIT codegen: Same as Windows, the libraries are less complete.
+- Windows, MacOS and Linux x64 w/ RyuJIT codegen: Simple apps. Check our [ASP.NET Core](samples/WebApi/) and [MonoGame](samples/MonoGame/) samples.
- Linux ARM w/ RyuJIT codegen: ElmSharp Hello Tizen application ([detailed status](https://github.com/dotnet/corert/issues/4856))
- CppCodeGen (targets all platforms that support C++): Simple C# programs. The big missing features are [reflection](https://github.com/dotnet/corert/issues/2035), [garbage collection](https://github.com/dotnet/corert/issues/2033) and [exception handling](https://github.com/dotnet/corert/issues/910).
- WebAssembly: Early prototype that compiles and runs very trivial programs only. Many features are [not yet implemented](https://github.com/dotnet/corert/issues?q=is%3Aissue+is%3Aopen+label%3Aarch-wasm).
diff --git a/buildscripts/build-managed.cmd b/buildscripts/build-managed.cmd
index e6258748a..c052aef78 100644
--- a/buildscripts/build-managed.cmd
+++ b/buildscripts/build-managed.cmd
@@ -46,6 +46,11 @@ IF ERRORLEVEL 1 exit /b %ERRORLEVEL%
"%__DotNetCliPath%\dotnet.exe" publish "%__SourceDir%\ILCompiler\netcoreapp\ilc.csproj" -r %__NugetRuntimeId% -o "%__RootBinDir%\%__BuildOS%.%__BuildArch%.%__BuildType%\tools"
IF ERRORLEVEL 1 exit /b %ERRORLEVEL%
+"%__DotNetCliPath%\dotnet.exe" restore "%__SourceDir%\ILVerify\netcoreapp\ILVerify.csproj" -r %__NugetRuntimeId%
+IF ERRORLEVEL 1 exit /b %ERRORLEVEL%
+"%__DotNetCliPath%\dotnet.exe" publish "%__SourceDir%\ILVerify\netcoreapp\ILVerify.csproj" -r %__NugetRuntimeId% -o "%__RootBinDir%\%__BuildOS%.%__BuildArch%.%__BuildType%\ILVerify"
+IF ERRORLEVEL 1 exit /b %ERRORLEVEL%
+
:: Set the environment for the managed build
call "!VS%__VSProductVersion%COMNTOOLS!\VsDevCmd.bat"
echo Commencing build of managed components for %__BuildOS%.%__BuildArch%.%__BuildType%
diff --git a/buildscripts/build-managed.sh b/buildscripts/build-managed.sh
index 5c0addc6f..9b53eb2f9 100755
--- a/buildscripts/build-managed.sh
+++ b/buildscripts/build-managed.sh
@@ -92,11 +92,11 @@ get_official_cross_builds()
return 0
fi
__tizenToolsRoot=${__ProjectRoot}/Tools/tizen
- __corefxsite="https://ci.dot.net/job/dotnet_corefx/job/master/view/Official%20Builds/job/"
- __coreclrsite="https://ci.dot.net/job/dotnet_coreclr/job/master/view/Official%20Builds/job/"
+ __corefxsite="https://ci.dot.net/job/dotnet_corefx/job/master/job/"
+ __coreclrsite="https://ci.dot.net/job/dotnet_coreclr/job/master/job/"
__buildArchiveName="build.tar.gz"
__systemNativeLibName="System.Native.a"
- __systemGlobNativeLibName="libSystem.Globalization.Native.a"
+ __systemGlobNativeLibName="System.Globalization.Native.a"
if [ $__BuildType = "Debug" ]; then
__buildtype="debug"
else
@@ -106,7 +106,7 @@ get_official_cross_builds()
__coreclrsource="armel_cross_${__buildtype}_tizen/lastSuccessfulBuild/artifact/bin/Product/Linux.armel.${__BuildType}/${__systemGlobNativeLibName}"
mkdir -p $__tizenToolsRoot
- (cd ${__tizenToolsRoot} && wget -N "${__corefxsite}${__corefxsource}")
+ (cd ${__tizenToolsRoot} && wget -t0 -N "${__corefxsite}${__corefxsource}")
export BUILDERRORLEVEL=$?
if [ $BUILDERRORLEVEL != 0 ]; then
exit $BUILDERRORLEVEL
@@ -114,7 +114,7 @@ get_official_cross_builds()
tar xvf ${__tizenToolsRoot}/${__buildArchiveName} -C ${__tizenToolsRoot} ./${__systemNativeLibName}
cp ${__tizenToolsRoot}/${__systemNativeLibName} $__ProjectRoot/bin/Linux.${__BuildArch}.${__BuildType}/framework
- (cd ${__tizenToolsRoot} && wget -N "${__coreclrsite}${__coreclrsource}")
+ (cd ${__tizenToolsRoot} && wget -t0 -N "${__coreclrsite}${__coreclrsource}")
export BUILDERRORLEVEL=$?
if [ $BUILDERRORLEVEL != 0 ]; then
exit $BUILDERRORLEVEL
diff --git a/buildscripts/buildvars-setup.cmd b/buildscripts/buildvars-setup.cmd
index 62beab9ba..6e52eda5d 100644
--- a/buildscripts/buildvars-setup.cmd
+++ b/buildscripts/buildvars-setup.cmd
@@ -2,6 +2,12 @@
set __BuildArch=x64
set __BuildType=Debug
set __BuildOS=Windows_NT
+set __HostOS=Windows_NT
+
+:: Disable telemetry, first time experience, and global sdk look for the CLI
+set DOTNET_CLI_TELEMETRY_OPTOUT=1
+set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+set DOTNET_MULTILEVEL_LOOKUP=0
:: Set the various build properties here so that CMake and MSBuild can pick them up
set "__ProjectDir=%~dp0.."
@@ -118,7 +124,7 @@ if defined VisualStudioVersion goto :RunVCVars
set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if exist %_VSWHERE% (
- for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools
+ for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath -products *`) do set _VSCOMNTOOLS=%%i\Common7\Tools
)
if not exist "%_VSCOMNTOOLS%" goto :MissingVersion
@@ -162,8 +168,8 @@ set Platform=
set __VCBuildArch=x86_amd64
if /i "%__BuildArch%" == "x86" (set __VCBuildArch=x86)
-set __NugetRuntimeId=win7-x64
-if /i "%__BuildArch%" == "x86" (set __NugetRuntimeId=win7-x86)
+set __NugetRuntimeId=win-x64
+if /i "%__BuildArch%" == "x86" (set __NugetRuntimeId=win-x86)
:Done
set BUILDVARS_DONE=1
diff --git a/buildscripts/buildvars-setup.sh b/buildscripts/buildvars-setup.sh
index 161556565..f41d79efc 100755
--- a/buildscripts/buildvars-setup.sh
+++ b/buildscripts/buildvars-setup.sh
@@ -68,8 +68,6 @@ get_current_linux_rid() {
# remove the last version digit
VERSION_ID=${VERSION_ID%.*}
rid=alpine.$VERSION_ID
- elif [[ $ID == "ubuntu" ]]; then
- rid=$ID.$VERSION_ID
fi
elif [ -e /etc/redhat-release ]; then
@@ -82,6 +80,10 @@ get_current_linux_rid() {
echo $rid
}
+# Disable telemetry, first time experience, and global sdk look for the CLI
+export DOTNET_CLI_TELEMETRY_OPTOUT=1
+export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+export DOTNET_MULTILEVEL_LOOKUP=0
export __scriptpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export __ProjectRoot=$__scriptpath/..
@@ -109,6 +111,12 @@ export __CrossBuild=0
__BuildArch=$__HostArch
+# Checking for any clang versions, if there is a symlink
+if [ -x "$(command -v clang)" ]; then
+ __ClangMajorVersion="$(echo | clang -dM -E - | grep __clang_major__ | cut -f3 -d ' ')"
+ __ClangMinorVersion="$(echo | clang -dM -E - | grep __clang_minor__ | cut -f3 -d ' ')"
+fi
+
while [ "$1" != "" ]; do
lowerI="$(echo $1 | awk '{print tolower($0)}')"
case $lowerI in
@@ -171,6 +179,22 @@ while [ "$1" != "" ]; do
export __ClangMajorVersion=3
export __ClangMinorVersion=9
;;
+ clang4.0)
+ export __ClangMajorVersion=4
+ export __ClangMinorVersion=0
+ ;;
+ clang5.0)
+ export __ClangMajorVersion=5
+ export __ClangMinorVersion=0
+ ;;
+ clang6.0)
+ export __ClangMajorVersion=6
+ export __ClangMinorVersion=0
+ ;;
+ clang7.0)
+ export __ClangMajorVersion=7
+ export __ClangMinorVersion=0
+ ;;
cross)
export __CrossBuild=1
;;
@@ -193,41 +217,49 @@ done
export $__BuildArch
-if [ $__BuildArch == "wasm" ]; then
- export __BuildOS=WebAssembly
-else
- # Use uname to determine what the OS is.
- export OSName=$(uname -s)
- case $OSName in
- Darwin)
- export __BuildOS=OSX
- export __NugetRuntimeId=osx.10.10-x64
- ulimit -n 2048
- ;;
-
- FreeBSD)
- export __BuildOS=FreeBSD
- # TODO: Add proper FreeBSD target
- export __NugetRuntimeId=ubuntu.14.04-x64
- ;;
-
- Linux)
- export __BuildOS=Linux
- export __NugetRuntimeId=$(get_current_linux_rid)-$__HostArch
- ;;
+# Use uname to determine what the OS is.
+export OSName=$(uname -s)
+case $OSName in
+ Darwin)
+ export __HostOS=OSX
+ export __NugetRuntimeId=osx-x64
+ ulimit -n 2048
+ ;;
+
+ FreeBSD)
+ export __HostOS=FreeBSD
+ # TODO: Add proper FreeBSD target
+ export __NugetRuntimeId=linux-x64
+ ;;
+
+ Linux)
+ export __HostOS=Linux
+ export __NugetRuntimeId=$(get_current_linux_rid)-$__HostArch
+ ;;
+
+ NetBSD)
+ export __HostOS=NetBSD
+ # TODO: Add proper NetBSD target
+ export __NugetRuntimeId=linux-x64
+ ;;
+
+ *)
+ echo "Unsupported OS $OSName detected, configuring as if for Linux"
+ export __HostOS=Linux
+ export __NugetRuntimeId=linux-x64
+ ;;
+esac
+
+# For msbuild
+if [ $__HostOS != "OSX" ]; then
+ export CppCompilerAndLinker=clang-${__ClangMajorVersion}.${__ClangMinorVersion}
+fi
- NetBSD)
- export __BuildOS=NetBSD
- # TODO: Add proper NetBSD target
- export __NugetRuntimeId=ubuntu.14.04-x64
- ;;
+export __BuildOS="$__HostOS"
- *)
- echo "Unsupported OS $OSName detected, configuring as if for Linux"
- export __BuildOS=Linux
- export __NugetRuntimeId=ubuntu.14.04-x64
- ;;
- esac
+# Overwrite __BuildOS with WebAssembly if wasm is target build arch, but keep the __NugetRuntimeId to match the Host OS
+if [ $__BuildArch == "wasm" ]; then
+ export __BuildOS=WebAssembly
fi
# If neither managed nor native are passed as arguments, default to building both
diff --git a/cross/arm/toolchain.cmake b/cross/arm/toolchain.cmake
index 35611cc50..772148750 100644
--- a/cross/arm/toolchain.cmake
+++ b/cross/arm/toolchain.cmake
@@ -9,14 +9,14 @@ add_compile_options(-mthumb)
add_compile_options(-mfpu=vfpv3)
add_compile_options(--sysroot=${CROSS_ROOTFS})
-set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -target arm-linux-gnueabihf")
+set(CROSS_LINK_FLAGS "-target arm-linux-gnueabihf")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/arm-linux-gnueabihf")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/arm-linux-gnueabihf")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
+set(CMAKE_EXE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_EXE_LINKER_FLAGS" FORCE)
+set(CMAKE_SHARED_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_SHARED_LINKER_FLAGS" FORCE)
+set(CMAKE_MODULE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_MODULE_LINKER_FLAGS" FORCE)
set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
diff --git a/cross/arm64/toolchain.cmake b/cross/arm64/toolchain.cmake
index cb92340f1..18c4caafa 100644
--- a/cross/arm64/toolchain.cmake
+++ b/cross/arm64/toolchain.cmake
@@ -7,14 +7,14 @@ set(CMAKE_SYSTEM_PROCESSOR aarch64)
add_compile_options(-target aarch64-linux-gnu)
add_compile_options(--sysroot=${CROSS_ROOTFS})
-set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -target aarch64-linux-gnu")
+set(CROSS_LINK_FLAGS "-target aarch64-linux-gnu")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/aarch64-linux-gnu")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/aarch64-linux-gnu")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
+set(CMAKE_EXE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_EXE_LINKER_FLAGS" FORCE)
+set(CMAKE_SHARED_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_SHARED_LINKER_FLAGS" FORCE)
+set(CMAKE_MODULE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_MODULE_LINKER_FLAGS" FORCE)
set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
diff --git a/cross/armel/toolchain.cmake b/cross/armel/toolchain.cmake
index 1dc6ec5d8..ac42b54dd 100644
--- a/cross/armel/toolchain.cmake
+++ b/cross/armel/toolchain.cmake
@@ -19,7 +19,7 @@ add_compile_options(-mthumb)
add_compile_options(-mfpu=vfpv3)
add_compile_options(--sysroot=${CROSS_ROOTFS})
-set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -target ${TOOLCHAIN}")
+set(CROSS_LINK_FLAGS "-target ${TOOLCHAIN}")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}")
if("$ENV{__DistroRid}" MATCHES "tizen.*")
@@ -37,9 +37,9 @@ else()
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/usr/lib/gcc/${TOOLCHAIN}/4.9")
endif()
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
+set(CMAKE_EXE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_EXE_LINKER_FLAGS" FORCE)
+set(CMAKE_SHARED_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_SHARED_LINKER_FLAGS" FORCE)
+set(CMAKE_MODULE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_MODULE_LINKER_FLAGS" FORCE)
set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
diff --git a/cross/x86/toolchain.cmake b/cross/x86/toolchain.cmake
index 1389a768f..ccc3a0356 100644
--- a/cross/x86/toolchain.cmake
+++ b/cross/x86/toolchain.cmake
@@ -7,14 +7,14 @@ add_compile_options("-m32")
add_compile_options("--sysroot=${CROSS_ROOTFS}")
add_compile_options("-Wno-error=unused-command-line-argument")
-set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}")
+set(CROSS_LINK_FLAGS "--sysroot=${CROSS_ROOTFS}")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/i686-linux-gnu")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/i386-linux-gnu")
set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -m32")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
-set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE)
+set(CMAKE_EXE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_EXE_LINKER_FLAGS" FORCE)
+set(CMAKE_SHARED_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_SHARED_LINKER_FLAGS" FORCE)
+set(CMAKE_MODULE_LINKER_FLAGS "${CROSS_LINK_FLAGS}" CACHE STRING "TOOLCHAIN_MODULE_LINKER_FLAGS" FORCE)
set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
diff --git a/dependencies.props b/dependencies.props
index dd8d755ce..6d97c96df 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -1,10 +1,10 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
- <RyuJITVersion>2.1.0-preview1-26030-01</RyuJITVersion>
+ <RyuJITVersion>2.1.0-preview2-26207-13</RyuJITVersion>
<ObjectWriterVersion>1.0.19-prerelease-00001</ObjectWriterVersion>
- <CoreFxVersion>4.5.0-preview1-26029-02</CoreFxVersion>
- <CoreFxUapVersion>4.6.0-preview1-26029-02</CoreFxUapVersion>
- <MicrosoftNETCoreNativeVersion>2.1.0-preview1-26030-01</MicrosoftNETCoreNativeVersion>
+ <CoreFxVersion>4.5.0-preview2-26202-05</CoreFxVersion>
+ <CoreFxUapVersion>4.6.0-preview2-26202-05</CoreFxUapVersion>
+ <MicrosoftNETCoreNativeVersion>2.1.0-preview2-26207-13</MicrosoftNETCoreNativeVersion>
<MicrosoftNETCoreAppPackageVersion>2.0.0</MicrosoftNETCoreAppPackageVersion>
<XunitNetcoreExtensionsVersion>1.0.1-prerelease-02104-02</XunitNetcoreExtensionsVersion>
</PropertyGroup>
diff --git a/init-tools.sh b/init-tools.sh
index ed2cffd5d..969c98548 100755
--- a/init-tools.sh
+++ b/init-tools.sh
@@ -130,11 +130,11 @@ if [ ! -e $__INIT_TOOLS_DONE_MARKER ]; then
fi
echo "Initializing BuildTools..."
- echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR" >> $__init_tools_log
+ echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR $__PACKAGES_DIR" >> $__init_tools_log
# Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424
chmod +x $__BUILD_TOOLS_PATH/init-tools.sh
- $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR >> $__init_tools_log
+ $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR $__PACKAGES_DIR >> $__init_tools_log
if [ "$?" != "0" ]; then
echo "ERROR: An error occurred when trying to initialize the tools." 1>&2
display_error_message
diff --git a/samples/MonoGame/NeonShooter.csproj b/samples/MonoGame/NeonShooter.csproj
new file mode 100644
index 000000000..3d6614148
--- /dev/null
+++ b/samples/MonoGame/NeonShooter.csproj
@@ -0,0 +1,92 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ <DefineConstants>$(DefineConstants);WINDOWS;LINUX</DefineConstants>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <RdXmlFile Include="rd.xml" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game.cs">
+ <Link>Game.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Art.cs">
+ <Link>Game\Art.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\BlackHole.cs">
+ <Link>Game\BlackHole.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\BloomComponent.cs">
+ <Link>Game\BloomComponent.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\BloomSettings.cs">
+ <Link>Game\BloomSettings.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Bullet.cs">
+ <Link>Game\Bullet.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\ColorUtil.cs">
+ <Link>Game\ColorUtil.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Enemy.cs">
+ <Link>Game\Enemy.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\EnemySpawner.cs">
+ <Link>Game\EnemySpawner.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Entity.cs">
+ <Link>Game\Entity.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\EntityManager.cs">
+ <Link>Game\EntityManager.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Extensions.cs">
+ <Link>Game\Extensions.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Grid.cs">
+ <Link>Game\Grid.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Input.cs">
+ <Link>Game\Input.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\MathUtil.cs">
+ <Link>Game\MathUtil.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\ParticleManager.cs">
+ <Link>Game\ParticleManager.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\ParticleState.cs">
+ <Link>Game\ParticleState.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\PlayerShip.cs">
+ <Link>Game\PlayerShip.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\PlayerStatus.cs">
+ <Link>Game\PlayerStatus.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Game\Sound.cs">
+ <Link>Game\Sound.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\NeonShooter\Program.cs">
+ <Link>Program.cs</Link>
+ </Compile>
+ </ItemGroup>
+
+ <ItemGroup>
+ <MonoGameContentReference Include="MonoGame.Samples\NeonShooter\Content\NeonShooter.mgcb">
+ <Link>Content\NeonShooter.mgcb</Link>
+ </MonoGameContentReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-*" />
+ <PackageReference Include="MonoGame.Content.Builder" Version="3.7.0.1" />
+ <PackageReference Include="MonoGame.Framework.DesktopGL.Core" Version="3.7.0.3" />
+ </ItemGroup>
+
+</Project>
diff --git a/samples/MonoGame/Platformer2D.csproj b/samples/MonoGame/Platformer2D.csproj
new file mode 100644
index 000000000..b81551942
--- /dev/null
+++ b/samples/MonoGame/Platformer2D.csproj
@@ -0,0 +1,71 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ <DefineConstants>$(DefineConstants);WINDOWS;LINUX</DefineConstants>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <RdXmlFile Include="rd.xml" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Accelerometer.cs">
+ <Link>Game\Accelerometer.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Animation.cs">
+ <Link>Game\Animation.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\AnimationPlayer.cs">
+ <Link>Game\AnimationPlayer.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Circle.cs">
+ <Link>Game\Circle.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Enemy.cs">
+ <Link>Game\Enemy.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Gem.cs">
+ <Link>Game\Gem.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Level.cs">
+ <Link>Game\Level.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Player.cs">
+ <Link>Game\Player.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\RectangleExtensions.cs">
+ <Link>Game\RectangleExtensions.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\Tile.cs">
+ <Link>Game\Tile.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\TouchCollectionExtensions.cs">
+ <Link>Game\TouchCollectionExtensions.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game.cs">
+ <Link>Game.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Game\VirtualGamePad.cs">
+ <Link>Game\VirtualGamePad.cs</Link>
+ </Compile>
+ <Compile Include="MonoGame.Samples\Platformer2D\Program.cs">
+ <Link>Program.cs</Link>
+ </Compile>
+ </ItemGroup>
+
+ <ItemGroup>
+ <MonoGameContentReference Include="MonoGame.Samples\Platformer2D\Content\Platformer2D.mgcb">
+ <Link>Content\Platformer2D.mgcb</Link>
+ </MonoGameContentReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-*" />
+ <PackageReference Include="MonoGame.Content.Builder" Version="3.7.0.1" />
+ <PackageReference Include="MonoGame.Framework.DesktopGL.Core" Version="3.7.0.3" />
+ </ItemGroup>
+
+</Project>
diff --git a/samples/MonoGame/README.md b/samples/MonoGame/README.md
new file mode 100644
index 000000000..2291d2bed
--- /dev/null
+++ b/samples/MonoGame/README.md
@@ -0,0 +1,94 @@
+# Building a MonoGame app with CoreRT
+
+This document will guide you through compiling a .NET Core [MonoGame](http://www.monogame.net) game with CoreRT.
+
+## Install the .NET Core SDK
+CoreRT is an AOT-optimized .NET Core runtime. If you're new to .NET Core make sure to visit the [official starting page](http://dotnet.github.io). It will guide you through installing pre-requisites and building your first app.
+If you're already familiar with .NET Core make sure you've [downloaded and installed the .NET Core 2 SDK](https://www.microsoft.com/net/download/core).
+
+## Create .NET Core MonoGame project
+Open a new shell/command prompt window and run the following commands.
+```bash
+> dotnet new --install MonoGame.Template.CSharp
+> dotnet new mgdesktopgl -o MyGame
+> cd MyGame
+```
+
+This will install .NET Core MonoGame template and create empty game project. .NET Core MonoGame port lives at https://github.com/cra0zy/MonoGame/tree/core currently. Thank you @cra0zy for the great work!
+
+Verify that the empty game builds and runs. You should see blue window:
+
+```bash
+> dotnet run
+```
+
+MonoGame tools require [Mono](http://www.mono-project.com/download/) on non-Windows platforms.
+
+## Add CoreRT to your project
+Using CoreRT to compile your application is done via the ILCompiler NuGet package, which is [published to MyGet with the CoreRT daily builds](https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.DotNet.ILCompiler).
+For the compiler to work, it first needs to be added to your project.
+
+In your shell/command prompt navigate to the root directory of your project and run the command:
+
+```bash
+> dotnet new nuget
+```
+
+This will add a nuget.config file to your application. Open the file and in the ``<packageSources> `` element under ``<clear/>`` add the following:
+
+```xml
+<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
+<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
+```
+
+Once you've added the package source, add a reference to the compiler by running the following command:
+
+```bash
+> dotnet add package Microsoft.DotNet.ILCompiler -v 1.0.0-alpha-*
+```
+
+## Restore and Publish your app
+
+Once the package has been successfully added it's time to compile and publish your app! In the shell/command prompt window, run the following command:
+
+```bash
+> dotnet publish -r <RID> -c <Configuration>
+```
+
+where `<Configuration>` is your project configuration (such as Debug or Release) and `<RID>` is the runtime identifier (one of win-x64, linux-x64, osx-x64). For example, if you want to publish a release configuration of your app for a 64-bit version of Windows the command would look like:
+
+```bash
+> dotnet publish -r win-x64 -c release
+```
+
+Once completed, you can find the native executable in the root folder of your project under `/bin/x64/<Configuration>/netcoreapp2.0/publish/`. Navigate to `/bin/x64/<Configuration>/netcoreapp2.0/publish/` in your project folder and run the produced native executable.
+
+## Try MonoGame sample game
+
+Clone MonoGame samples from github:
+
+```bash
+> git clone https://github.com/MonoGame/MonoGame.Samples
+```
+
+MonoGame samples include project files for number of targets, but not for .NET Core yet. One has to create the project using above steps and transplant links to sample sources and assets to it. This directory contains .NET Core project for Platformer2D and NeonShooter samples that assume MonoGame.Samples is cloned under it. Build it and start playing!
+
+```bash
+> dotnet publish -r win-x64 -c release Platformer2D.csproj
+> bin\x64\Release\netcoreapp2.0\publish\Platformer2D.exe
+```
+
+The NeonShooter sample works on Windows-only due to https://github.com/MonoGame/MonoGame/issues/3270.
+
+## Using reflection
+Runtime directives are XML configuration files, which specify which otherwise unreachable elements of your program are available for reflection. They are used at compile-time to enable AOT compilation in applications at runtime. The runtime directives are reference in the project via RdXmlFile item:
+
+```xml
+<ItemGroup>
+ <RdXmlFile Include="rd.xml" />
+</ItemGroup>
+```
+
+MonoGame serialization engine uses reflection to create types representing the game assets that needs to mentioned in the [rd.xml](rd.xml) file. If you see MissingMetadataException thrown during game startup, add the missing types to the rd.xml file.
+
+Feel free to modify the sample application and experiment. However, keep in mind some functionality might not yet be supported in CoreRT. Let us know on the [Issues page](https://github.com/dotnet/corert/issues/).
diff --git a/samples/MonoGame/nuget.config b/samples/MonoGame/nuget.config
new file mode 100644
index 000000000..d8d3f2b01
--- /dev/null
+++ b/samples/MonoGame/nuget.config
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <packageSources>
+ <!--To inherit the global NuGet package sources remove the <clear/> line below -->
+ <clear />
+ <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
+ </packageSources>
+</configuration>
diff --git a/samples/MonoGame/rd.xml b/samples/MonoGame/rd.xml
new file mode 100644
index 000000000..051a95f9a
--- /dev/null
+++ b/samples/MonoGame/rd.xml
@@ -0,0 +1,12 @@
+<Directives>
+ <Application>
+ <Assembly Name="MonoGame.Framework">
+ <Type Name="Microsoft.Xna.Framework.Content.EffectReader" Dynamic="Required All" />
+ <Type Name="Microsoft.Xna.Framework.Content.SpriteFontReader" Dynamic="Required All" />
+ <Type Name="Microsoft.Xna.Framework.Content.Texture2DReader" Dynamic="Required All" />
+ <Type Name="Microsoft.Xna.Framework.Content.RectangleReader" Dynamic="Required All" />
+ <Type Name="Microsoft.Xna.Framework.Content.ListReader`1[[System.Char,mscorlib]]" Dynamic="Required All" />
+ </Assembly>
+ <Assembly Name="mscorlib" />
+ </Application>
+</Directives>
diff --git a/samples/WebApi/README.md b/samples/WebApi/README.md
index a47f25e12..5c5ca2ed0 100644
--- a/samples/WebApi/README.md
+++ b/samples/WebApi/README.md
@@ -75,10 +75,10 @@ If your application makes use of reflection, you will need to create a rd.xml fi
At runtime, if a method or type is not found or cannot be loaded, an exception will be thrown. The exception message will contain information on the missing type reference, which you can then add to the rd.xml of your program.
-Once you've created a rd.xml file, navigate to the root directory of your project and open its `.csproj` file and in the first `<PropertyGroup>` element add the following:
+Once you've created a rd.xml file, navigate to the root directory of your project and open its `.csproj` file and in the first `<ItemGroup>` element add the following:
```xml
-<RdXmlFile>path_to_rdxml_file\rd.xml</RdXmlFile>
+<RdXmlFile Include="path_to_rdxml_file\rd.xml" />
```
where path_to_rdxml_file is the location of the file on your disk.
@@ -120,13 +120,13 @@ public class ValuesController
## Restore and Publish your app
-Once the package has been successfully added it's time to compile and publish your app! If you're using Windows, make sure you're using `x64 Native Tools Command Prompt for VS 2017` instead of the standard Windows command prompt. In the shell/command prompt window, run the following command:
+Once the package has been successfully added it's time to compile and publish your app! In the shell/command prompt window, run the following command:
```bash
> dotnet publish -r <RID> -c <Configuration>
```
-where `<Configuration>` is your project configuration (such as Debug or Release) and `<RID>` is the runtime identifier, which you specified in the csproj file (one of win-x64, linux-x64, osx-x64). For example, if you want to publish a release configuration of your app for a 64-bit version of Windows the command would look like:
+where `<Configuration>` is your project configuration (such as Debug or Release) and `<RID>` is the runtime identifier (one of win-x64, linux-x64, osx-x64). For example, if you want to publish a release configuration of your app for a 64-bit version of Windows the command would look like:
```bash
> dotnet publish -r win-x64 -c release
@@ -136,6 +136,8 @@ Once completed, you can find the native executable in the root folder of your pr
## Try it out!
+If you are running macOS, make sure you have [libuv](https://github.com/libuv/libuv) installed, as ASP.NET is built on top of libuv. You can use [homebrew](https://brew.sh/) to get it (`brew install libuv`).
+
Navigate to `/bin/x64/<Configuration>/netcoreapp2.0/publish/` in your project folder and run the produced executable. It should display "Now listening on: http://localhost:XXXX" with XXXX being a port on your machine. Open your browser and navigate to that URL. You should see "Hello World!" displayed in your browser.
Feel free to modify the sample application and experiment. However, keep in mind some functionality might not yet be supported in CoreRT. Let us know on the [Issues page](https://github.com/dotnet/corert/issues/).
diff --git a/samples/WebApi/SampleWebApi.csproj b/samples/WebApi/SampleWebApi.csproj
index d485b393d..724c9b9c7 100644
--- a/samples/WebApi/SampleWebApi.csproj
+++ b/samples/WebApi/SampleWebApi.csproj
@@ -2,10 +2,13 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
- <RdXmlFile>rd.xml</RdXmlFile>
</PropertyGroup>
<ItemGroup>
+ <RdXmlFile Include="rd.xml" />
+ </ItemGroup>
+
+ <ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets
index 12743b911..5a801d765 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets
@@ -50,6 +50,9 @@
<UsingTask TaskName="ComputeManagedAssemblies" AssemblyFile="$(IlcBuildTasksPath)" />
<Target Name="_ComputeAssembliesToCompileToNative" DependsOnTargets="$(IlcDynamicBuildPropertyDependencies)">
+ <!-- Fail with descriptive error message for common mistake. -->
+ <Error Condition="'$(RuntimeIdentifier)' == ''" Text="RuntimeIdentifier is required for native compilation. Try running dotnet publish with the -r option value specified." />
+
<!-- CoreRT SDK and Framework Assemblies need to be defined to avoid CoreCLR implementations being set as compiler inputs -->
<Error Condition="'@(PrivateSdkAssemblies)' == ''" Text="The PrivateSdkAssemblies ItemGroup is required for _ComputeAssembliesToCompileToNative" />
<Error Condition="'@(FrameworkAssemblies)' == ''" Text="The FrameworkAssemblies ItemGroup is required for _ComputeAssembliesToCompileToNative" />
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
index 69539c5e1..441f4754c 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
@@ -33,6 +33,11 @@ See the LICENSE file in the project root for more information.
</PropertyGroup>
<Target Name="SetupOSSpecificProps" DependsOnTargets="$(IlcDynamicBuildPropertyDependencies)">
+ <PropertyGroup>
+ <NativeLibraryExtension Condition="'$(NativeCodeGen)' != 'wasm'">.a</NativeLibraryExtension>
+ <NativeLibraryExtension Condition="'$(NativeCodeGen)' == 'wasm'">.bc</NativeLibraryExtension>
+ </PropertyGroup>
+
<ItemGroup>
<CppCompilerAndLinkerArg Include="-I $(IlcPath)/inc" />
<CppCompilerAndLinkerArg Condition="'$(Configuration)' == 'Debug'" Include="-g -O0" />
@@ -48,24 +53,28 @@ See the LICENSE file in the project root for more information.
<NativeLibrary Condition="$(NativeCodeGen) == ''" Include="$(IlcPath)/sdk/libRuntime.a" />
<NativeLibrary Condition="$(NativeCodeGen) == 'cpp'" Include="$(IlcPath)/sdk/libbootstrappercpp.a" />
<NativeLibrary Condition="$(NativeCodeGen) == 'cpp'" Include="$(IlcPath)/sdk/libPortableRuntime.a" />
- <NativeLibrary Condition="$(NativeCodeGen) == 'wasm'" Include="$(IlcPath)/sdk/libbootstrappercpp.a" />
- <NativeLibrary Condition="$(NativeCodeGen) == 'wasm'" Include="$(IlcPath)/sdk/libPortableRuntime.a" />
+ <NativeLibrary Condition="$(NativeCodeGen) == 'wasm'" Include="$(IlcPath)/sdk/libbootstrappercpp.bc" />
+ <NativeLibrary Condition="$(NativeCodeGen) == 'wasm'" Include="$(IlcPath)/sdk/libPortableRuntime.bc" />
</ItemGroup>
<ItemGroup>
- <AdditionalNativeLibrary Include="$(IlcPath)/sdk/libSystem.Private.CoreLib.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Globalization.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.IO.Compression.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Net.Http.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Net.Security.Native.a" />
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Security.Cryptography.Native.Apple.a" Condition="'$(TargetOS)' == 'OSX'"/>
- <AdditionalNativeLibrary Include="$(IlcPath)/framework/System.Security.Cryptography.Native.OpenSsl.a" Condition="'$(TargetOS)' != 'OSX'"/>
+ <NativeLibrary Include="$(IlcPath)/sdk/libSystem.Private.CoreLib.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.Globalization.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.IO.Compression.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.Net.Http.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.Net.Security.Native$(NativeLibraryExtension)" />
+ <NativeLibrary Include="$(IlcPath)/framework/System.Security.Cryptography.Native.Apple$(NativeLibraryExtension)" Condition="'$(TargetOS)' == 'OSX'"/>
+ <NativeLibrary Include="$(IlcPath)/framework/System.Security.Cryptography.Native.OpenSsl$(NativeLibraryExtension)" Condition="'$(TargetOS)' != 'OSX'"/>
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(TargetOS)' == 'OSX'">
+ <NativeFramework Include="CoreFoundation" />
+ <NativeFramework Include="Security" />
</ItemGroup>
<ItemGroup>
<LinkerArg Include="@(NativeLibrary)" />
- <LinkerArg Include="@(AdditionalNativeLibrary)" />
<LinkerArg Include="-g" />
<LinkerArg Include="-Wl,-rpath,'$ORIGIN'" />
<LinkerArg Include="-pthread" />
@@ -79,6 +88,7 @@ See the LICENSE file in the project root for more information.
<LinkerArg Include="-licucore" Condition="'$(TargetOS)' == 'OSX'" />
<LinkerArg Include="-dynamiclib" Condition="'$(TargetOS)' == 'OSX' and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-shared" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" />
+ <LinkerArg Include="@(NativeFramework->'-framework %(Identity)')" Condition="'$(TargetOS)' == 'OSX'" />
</ItemGroup>
<Exec Command="command -v $(CppLinker)" IgnoreExitCode="true">
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
index 0182d5518..eadca8826 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
@@ -45,24 +45,23 @@ See the LICENSE file in the project root for more information.
</ItemGroup>
<ItemGroup>
- <AdditionalNativeLibrary Include="kernel32.lib" />
- <AdditionalNativeLibrary Include="user32.lib" />
- <AdditionalNativeLibrary Include="gdi32.lib" />
- <AdditionalNativeLibrary Include="winspool.lib" />
- <AdditionalNativeLibrary Include="comdlg32.lib" />
- <AdditionalNativeLibrary Include="advapi32.lib" />
- <AdditionalNativeLibrary Include="shell32.lib" />
- <AdditionalNativeLibrary Include="ole32.lib" />
- <AdditionalNativeLibrary Include="oleaut32.lib" />
- <AdditionalNativeLibrary Include="uuid.lib" />
- <AdditionalNativeLibrary Include="bcrypt.lib" />
- <AdditionalNativeLibrary Include="normaliz.lib" />
+ <NativeLibrary Include="kernel32.lib" />
+ <NativeLibrary Include="user32.lib" />
+ <NativeLibrary Include="gdi32.lib" />
+ <NativeLibrary Include="winspool.lib" />
+ <NativeLibrary Include="comdlg32.lib" />
+ <NativeLibrary Include="advapi32.lib" />
+ <NativeLibrary Include="shell32.lib" />
+ <NativeLibrary Include="ole32.lib" />
+ <NativeLibrary Include="oleaut32.lib" />
+ <NativeLibrary Include="uuid.lib" />
+ <NativeLibrary Include="bcrypt.lib" />
+ <NativeLibrary Include="normaliz.lib" />
</ItemGroup>
<ItemGroup>
<LinkerArg Condition="$(NativeLib) == 'Shared'" Include="/DLL" />
<LinkerArg Include="@(NativeLibrary)" />
- <LinkerArg Include="@(AdditionalNativeLibrary)" />
<LinkerArg Include="/NOLOGO /DEBUG /MANIFEST:NO" />
<!-- The runtime is not compatible with jump stubs inserted by incremental linking. -->
<LinkerArg Include="/INCREMENTAL:NO" />
@@ -77,9 +76,28 @@ See the LICENSE file in the project root for more information.
<LinkerArg Include="/OPT:ICF" />
</ItemGroup>
- <Exec Command="where /Q $(CppLinker)" IgnoreExitCode="true">
- <Output TaskParameter="ExitCode" PropertyName="_WhereLinker"/>
+ <Exec Command="where /Q $(CppCompiler) &amp;&amp; where /Q $(CppLinker)" IgnoreExitCode="true">
+ <Output TaskParameter="ExitCode" PropertyName="_WhereCppTools"/>
</Exec>
- <Error Condition="'$(_WhereLinker)' != '0'" Text="Platform linker not found. Make sure to publish from a x64 Native Tools Command Prompt for VS 2017 with C++ tools installed." />
+
+ <Message Condition="'$(_WhereCppTools)' != '0'" Text="Tools '$(CppCompiler)' and '$(CppLinker)' not found on PATH. Attempting to autodetect." />
+
+ <Exec Condition="'$(_WhereCppTools)' != '0'" Command="&quot;$(MSBuildThisFileDirectory)findvcvarsall.bat&quot; amd64" IgnoreExitCode="true" ConsoleToMSBuild="true" StandardOutputImportance="Low">
+ <Output TaskParameter="ConsoleOutput" PropertyName="_FindVCVarsallOutput"/>
+ <Output TaskParameter="ExitCode" PropertyName="_VCVarsAllFound"/>
+ </Exec>
+
+ <ItemGroup Condition="'$(_VCVarsAllFound)' == '0'">
+ <AdditionalNativeLibraryDirectories Include="$(_FindVCVarsallOutput.Split(`#`)[1].Split(';'))" />
+ </ItemGroup>
+
+ <PropertyGroup Condition="'$(_VCVarsAllFound)' == '0'">
+ <_CppToolsDirectory>$(_FindVCVarsallOutput.Split(`#`)[0])</_CppToolsDirectory>
+ <CppCompiler>"$(_CppToolsDirectory)cl.exe"</CppCompiler>
+ <CppLinker>"$(_CppToolsDirectory)link.exe"</CppLinker>
+ <CppLibCreator>"$(_CppToolsDirectory)lib.exe"</CppLibCreator>
+ </PropertyGroup>
+
+ <Error Condition="'$(_WhereCppTools)' != '0' AND '$(_VCVarsAllFound)' == '1'" Text="Platform linker not found. To fix this problem, download and install Visual Studio 2017 from http://visualstudio.com. Make sure to install the Desktop Development for C++ workload." />
</Target>
</Project>
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.targets b/src/BuildIntegration/Microsoft.NETCore.Native.targets
index 6e03efac7..23b817c7a 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -141,7 +141,7 @@ See the LICENSE file in the project root for more information.
</Target>
<Target Name="IlcCompile"
- Inputs="@(IlcCompileInput)"
+ Inputs="@(IlcCompileInput);@(RdXmlFile)"
Outputs="$(NativeIntermediateOutputPath)%(ManagedBinary.Filename)$(IlcOutputFileExt)"
DependsOnTargets="$(IlcCompileDependsOn)">
@@ -155,7 +155,7 @@ See the LICENSE file in the project root for more information.
<IlcArg Condition="$(Optimize) == 'true'" Include="-O" />
<IlcArg Condition="$(DebugSymbols) == 'true'" Include="-g" />
<IlcArg Condition="$(IlcGenerateMapFile) == 'true'" Include="--map:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).map.xml" />
- <IlcArg Condition="$(RdXmlFile) != ''" Include="--rdxml:$(RdXmlFile)" />
+ <IlcArg Include="@(RdXmlFile->'--rdxml:%(Identity)')" />
<IlcArg Condition="$(OutputType) == 'Library' and $(NativeLib) != ''" Include="--nativelib" />
<IlcArg Condition="$(ExportsFile) != ''" Include="--exportsfile:$(ExportsFile)" />
<ILcArg Condition="'$(Platform)' == 'wasm'" Include="--wasm" />
@@ -202,6 +202,7 @@ See the LICENSE file in the project root for more information.
<CustomLinkerArg Include="-o $(NativeBinary)" Condition="'$(OS)' != 'Windows_NT'" />
<CustomLinkerArg Include="/OUT:$(NativeBinary)" Condition="'$(OS)' == 'Windows_NT'" />
<CustomLinkerArg Include="/DEF:$(ExportsFile)" Condition="'$(OS)' == 'Windows_NT' and $(ExportsFile) != ''" />
+ <CustomLinkerArg Include="/LIBPATH:&quot;%(AdditionalNativeLibraryDirectories.Identity)&quot;" Condition="'$(OS)' == 'Windows_NT' and '@(AdditionalNativeLibraryDirectories->Count())' &gt; 0" />
<CustomLinkerArg Include="-exported_symbols_list $(ExportsFile)" Condition="'$(OS)' != 'Windows_NT' and $(ExportsFile) != ''" />
<CustomLinkerArg Include="@(LinkerArg)" />
</ItemGroup>
@@ -214,8 +215,8 @@ See the LICENSE file in the project root for more information.
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName($(NativeBinary)))" />
- <Exec Command="$(CppLinker) @(CustomLinkerArg, ' ')" Condition="'$(OS)' != 'Windows_NT' and '$(NativeLib)' != 'Static'" />
- <Exec Command="$(CppLibCreator) @(CustomLibArg, ' ')" Condition="'$(OS)' != 'Windows_NT' and '$(NativeLib)' == 'Static'" />
+ <Exec Command="$(CppLinker) @(CustomLinkerArg, ' ')" Condition="'$(OS)' != 'Windows_NT' and '$(NativeLib)' != 'Static' and '$(NativeCodeGen)' != 'wasm'" />
+ <Exec Command="$(CppLibCreator) @(CustomLibArg, ' ')" Condition="'$(OS)' != 'Windows_NT' and '$(NativeLib)' == 'Static' and '$(NativeCodeGen)' != 'wasm'" />
<WriteLinesToFile File="$(NativeIntermediateOutputPath)link.rsp" Lines="@(CustomLinkerArg)" Overwrite="true" Condition="'$(OS)' == 'Windows_NT' and '$(NativeLib)' != 'Static'" />
<Exec Command="$(CppLinker) @&quot;$(NativeIntermediateOutputPath)link.rsp&quot;" Condition="'$(OS)' == 'Windows_NT' and '$(NativeLib)' != 'Static' and '$(NativeCodeGen)' != 'wasm'" />
@@ -224,12 +225,13 @@ See the LICENSE file in the project root for more information.
<PropertyGroup>
<EmccArgs>&quot;$(NativeObject)&quot; -o &quot;$(NativeBinary)&quot; -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 </EmccArgs>
- <EmccArgs Condition="'$(Platform)'=='wasm'">$(EmccArgs) &quot;$(IlcPath)\sdk\libPortableRuntime.bc&quot; &quot;$(IlcPath)\sdk\libbootstrappercpp.bc&quot; </EmccArgs>
+ <EmccArgs Condition="'$(Platform)'=='wasm'">$(EmccArgs) &quot;$(IlcPath)/sdk/libPortableRuntime.bc&quot; &quot;$(IlcPath)/sdk/libbootstrappercpp.bc&quot; </EmccArgs>
<EmccArgs Condition="'$(Configuration)'=='Release'">$(EmccArgs) -O2 --llvm-lto 2</EmccArgs>
<EmccArgs Condition="'$(Configuration)'=='Debug'">$(EmccArgs) -g3</EmccArgs>
</PropertyGroup>
- <Exec Command="&quot;$(EMSCRIPTEN)\emcc.bat&quot; $(EmccArgs)" Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSCRIPTEN)' != ''" />
+ <Exec Command="&quot;$(EMSCRIPTEN)\emcc.bat&quot; $(EmccArgs)" Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSCRIPTEN)' != '' and '$(OS)' == 'Windows_NT'" />
+ <Exec Command="&quot;$(EMSCRIPTEN)/emcc&quot; $(EmccArgs)" Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSCRIPTEN)' != '' and '$(OS)' != 'Windows_NT'" />
<Message Text="Emscripten not found, not linking WebAssembly. To enable WebAssembly linking, install Emscripten and ensure the EMSCRIPTEN environment variable points to the directory containing emcc.bat"
Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSCRIPTEN)' == ''" />
</Target>
diff --git a/src/BuildIntegration/findvcvarsall.bat b/src/BuildIntegration/findvcvarsall.bat
new file mode 100644
index 000000000..91c3bfe33
--- /dev/null
+++ b/src/BuildIntegration/findvcvarsall.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+SETLOCAL
+
+SET vswherePath=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe
+IF NOT EXIST "%vswherePath%" GOTO :ERROR
+
+FOR /F "tokens=*" %%i IN ( '
+ "%vswherePath%" -latest -prerelease -products * ^
+ -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ^
+ -property installationPath'
+ ) DO SET vsBase=%%i
+
+IF "%vsBase%"=="" GOTO :ERROR
+
+CALL "%vsBase%\vc\Auxiliary\Build\vcvarsall.bat" %1% > NUL
+
+FOR /F "delims=" %%W IN ('where link') DO (
+ FOR %%A IN ("%%W") DO ECHO %%~dpA#
+ GOTO :CAPTURE_LIB_PATHS
+)
+
+:CAPTURE_LIB_PATHS
+ECHO %LIB%
+
+EXIT /B 0
+
+ENDLOCAL
+
+:ERROR
+ EXIT /B 1
diff --git a/src/Common/src/Internal/NativeFormat/NativeFormat.cs b/src/Common/src/Internal/NativeFormat/NativeFormat.cs
index 6a430c0c8..3c8539adc 100644
--- a/src/Common/src/Internal/NativeFormat/NativeFormat.cs
+++ b/src/Common/src/Internal/NativeFormat/NativeFormat.cs
@@ -16,7 +16,7 @@ using System;
//
// - Naturally compressed: Integers are stored using variable length encoding. Offsets are stored as relative offsets.
//
-// - Random accesss: Random access to selected information should be fast. It is achieved by using tokens as offsets.
+// - Random access: Random access to selected information should be fast. It is achieved by using tokens as offsets.
//
// - Locality: Access to related information should be accessing data that are close to each other.
//
diff --git a/src/Common/src/Internal/NativeFormat/NativeFormatWriter.cs b/src/Common/src/Internal/NativeFormat/NativeFormatWriter.cs
index bdc346607..c237fa62d 100644
--- a/src/Common/src/Internal/NativeFormat/NativeFormatWriter.cs
+++ b/src/Common/src/Internal/NativeFormat/NativeFormatWriter.cs
@@ -260,7 +260,7 @@ namespace Internal.NativeFormat
#endif
}
- // Aggresive phase that only allows offsets to shrink.
+ // Aggressive phase that only allows offsets to shrink.
_phase = SavePhase.Shrinking;
for (; ; )
{
diff --git a/src/Common/src/Internal/Text/Utf8StringBuilder.cs b/src/Common/src/Internal/Text/Utf8StringBuilder.cs
index 1fcca9450..0cdd95abc 100644
--- a/src/Common/src/Internal/Text/Utf8StringBuilder.cs
+++ b/src/Common/src/Internal/Text/Utf8StringBuilder.cs
@@ -78,6 +78,11 @@ namespace Internal.Text
return Encoding.UTF8.GetString(_buffer, 0, _length);
}
+ public string ToString(int start)
+ {
+ return Encoding.UTF8.GetString(_buffer, start, _length - start);
+ }
+
public Utf8String ToUtf8String()
{
var ret = new byte[_length];
@@ -98,5 +103,50 @@ namespace Internal.Text
Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length);
_buffer = newBuffer;
}
+
+ // Find the boundary of the last character prior to a position
+ // If pos points to the last byte of a char, then return pos; Otherwise,
+ // return the position of the last byte of the preceding char.
+ public int LastCharBoundary(int pos)
+ {
+ Debug.Assert(pos < _length);
+
+ if (_buffer[pos] < 128 /*10000000*/)
+ {
+ // This is a single byte character
+ return pos;
+ }
+
+ int origPos = pos;
+
+ // Skip following bytes of a multi-byte character until the first byte is seen
+ while (_buffer[pos] < 192 /*11000000*/)
+ {
+ pos--;
+ }
+
+ if (pos == origPos - 3)
+ {
+ // We just skipped a four-byte character
+ Debug.Assert(_buffer[pos] >= 240 /*11110000*/);
+ return origPos;
+ }
+
+ if (pos == origPos - 2 && _buffer[pos] < 240 && _buffer[pos] >= 224 /*11100000*/)
+ {
+ // We just skipped a three-byte character
+ return origPos;
+ }
+
+ if (pos == origPos - 1 && _buffer[pos] < 224)
+ {
+ // We just skipped a two-byte character
+ Debug.Assert(_buffer[pos] >= 192 /*11000000*/);
+ return origPos;
+ }
+
+ // We were in the middle of a multi-byte character
+ return pos - 1;
+ }
}
}
diff --git a/src/Common/src/Interop/Windows/mincore/Interop.MUI.cs b/src/Common/src/Interop/Windows/mincore/Interop.MUI.cs
deleted file mode 100644
index 247218b98..000000000
--- a/src/Common/src/Interop/Windows/mincore/Interop.MUI.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-
-internal static partial class Interop
-{
- internal static unsafe partial class mincore
- {
- internal const int MUI_LANGUAGE_ID = 0x4;
- internal const int MUI_LANGUAGE_NAME = 0x8;
- internal const int MUI_PREFERRED_UI_LANGUAGES = 0x10;
- internal const int MUI_INSTALLED_LANGUAGES = 0x20;
- internal const int MUI_ALL_LANGUAGES = 0x40;
- internal const int MUI_LANG_NEUTRAL_PE_FILE = 0x100;
- internal const int MUI_NON_LANG_NEUTRAL_FILE = 0x200;
-
- [DllImport("api-ms-win-core-localization-l1-2-1.dll", CharSet = CharSet.Unicode)]
- internal static extern bool GetFileMUIPath(int flags, String filePath, StringBuilder language, ref int languageLength, StringBuilder fileMuiPath, ref int fileMuiPathLength, ref Int64 enumerator);
- }
-} \ No newline at end of file
diff --git a/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.Win32.cs b/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.Win32.cs
deleted file mode 100644
index a7044fb3c..000000000
--- a/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.Win32.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-internal static partial class Interop
-{
- internal static unsafe partial class mincore
- {
- [StructLayout(LayoutKind.Sequential)]
- internal struct REGISTRY_TIME_ZONE_INFORMATION
- {
- public Int32 Bias;
- public Int32 StandardBias;
- public Int32 DaylightBias;
- public SYSTEMTIME StandardDate;
- public SYSTEMTIME DaylightDate;
-
- public REGISTRY_TIME_ZONE_INFORMATION(TIME_ZONE_INFORMATION tzi)
- {
- Bias = tzi.Bias;
- StandardDate = tzi.StandardDate;
- StandardBias = tzi.StandardBias;
- DaylightDate = tzi.DaylightDate;
- DaylightBias = tzi.DaylightBias;
- }
-
- public REGISTRY_TIME_ZONE_INFORMATION(Byte[] bytes)
- {
- //
- // typedef struct _REG_TZI_FORMAT {
- // [00-03] LONG Bias;
- // [04-07] LONG StandardBias;
- // [08-11] LONG DaylightBias;
- // [12-27] SYSTEMTIME StandardDate;
- // [12-13] WORD wYear;
- // [14-15] WORD wMonth;
- // [16-17] WORD wDayOfWeek;
- // [18-19] WORD wDay;
- // [20-21] WORD wHour;
- // [22-23] WORD wMinute;
- // [24-25] WORD wSecond;
- // [26-27] WORD wMilliseconds;
- // [28-43] SYSTEMTIME DaylightDate;
- // [28-29] WORD wYear;
- // [30-31] WORD wMonth;
- // [32-33] WORD wDayOfWeek;
- // [34-35] WORD wDay;
- // [36-37] WORD wHour;
- // [38-39] WORD wMinute;
- // [40-41] WORD wSecond;
- // [42-43] WORD wMilliseconds;
- // } REG_TZI_FORMAT;
- //
- if (bytes == null || bytes.Length != 44)
- {
- throw new ArgumentException(SR.Argument_InvalidREG_TZI_FORMAT, nameof(bytes));
- }
- Bias = ToInt32(bytes, 0);
- StandardBias = ToInt32(bytes, 4);
- DaylightBias = ToInt32(bytes, 8);
-
- StandardDate.wYear = (ushort)ToInt16(bytes, 12);
- StandardDate.wMonth = (ushort)ToInt16(bytes, 14);
- StandardDate.wDayOfWeek = (ushort)ToInt16(bytes, 16);
- StandardDate.wDay = (ushort)ToInt16(bytes, 18);
- StandardDate.wHour = (ushort)ToInt16(bytes, 20);
- StandardDate.wMinute = (ushort)ToInt16(bytes, 22);
- StandardDate.wSecond = (ushort)ToInt16(bytes, 24);
- StandardDate.wMilliseconds = (ushort)ToInt16(bytes, 26);
-
- DaylightDate.wYear = (ushort)ToInt16(bytes, 28);
- DaylightDate.wMonth = (ushort)ToInt16(bytes, 30);
- DaylightDate.wDayOfWeek = (ushort)ToInt16(bytes, 32);
- DaylightDate.wDay = (ushort)ToInt16(bytes, 34);
- DaylightDate.wHour = (ushort)ToInt16(bytes, 36);
- DaylightDate.wMinute = (ushort)ToInt16(bytes, 38);
- DaylightDate.wSecond = (ushort)ToInt16(bytes, 40);
- DaylightDate.wMilliseconds = (ushort)ToInt16(bytes, 42);
- }
-
- private static short ToInt16(byte[] value, int startIndex)
- {
- return (short)(value[startIndex] | (value[startIndex + 1] << 8));
- }
-
- private static int ToInt32(byte[] value, int startIndex)
- {
- return value[startIndex] | (value[startIndex + 1] << 8) | (value[startIndex + 2] << 16) | (value[startIndex + 3] << 24);
- }
- }
- }
-}
diff --git a/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.cs b/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.cs
index f40a5f4d4..e2c80e9ab 100644
--- a/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.cs
+++ b/src/Common/src/Interop/Windows/mincore/Interop.TimeZone.cs
@@ -75,7 +75,6 @@ internal static partial class Interop
internal const int TIME_ZONE_ID_UNKNOWN = 0;
internal const int TIME_ZONE_ID_STANDARD = 1;
internal const int TIME_ZONE_ID_DAYLIGHT = 2;
- internal const int MAX_PATH = 260;
[DllImport("api-ms-win-core-timezone-l1-1-0.dll")]
internal extern static uint EnumDynamicTimeZoneInformation(uint dwIndex, out TIME_DYNAMIC_ZONE_INFORMATION lpTimeZoneInformation);
diff --git a/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs b/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
index c635b0134..d27fbf6b4 100644
--- a/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
+++ b/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
@@ -274,15 +274,22 @@ namespace Internal.TypeSystem
return false;
}
- private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSig(MethodDesc targetMethod, DefType currentType)
+ /// <summary>
+ /// Find matching a matching method by name and sig on a type. (Restricted to virtual methods only)
+ /// </summary>
+ /// <param name="targetMethod"></param>
+ /// <param name="currentType"></param>
+ /// <param name="reverseMethodSearch">Used to control the order of the search. For historical purposes to
+ /// match .NET Framework behavior, this is typically true, but not always. There is no particular rationale
+ /// for the particular orders other than to attempt to be consistent in virtual method override behavior
+ /// betweeen runtimes.</param>
+ /// <param name="nameSigMatchMethodIsValidCandidate"></param>
+ /// <returns></returns>
+ private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSig(MethodDesc targetMethod, DefType currentType, bool reverseMethodSearch, Func<MethodDesc, MethodDesc, bool> nameSigMatchMethodIsValidCandidate)
{
string name = targetMethod.Name;
MethodSignature sig = targetMethod.Signature;
- // TODO: InstantiatedType.GetMethod can't handle this for a situation like
- // an instantiation of Foo<T>.M(T) because sig is instantiated, but it compares
- // it to the uninstantiated version
- //MethodDesc implMethod = currentType.GetMethod(name, sig);
MethodDesc implMethod = null;
foreach (MethodDesc candidate in currentType.GetAllMethods())
{
@@ -293,11 +300,15 @@ namespace Internal.TypeSystem
{
if (candidate.Signature.Equals(sig))
{
- if (implMethod != null)
+ if (nameSigMatchMethodIsValidCandidate == null || nameSigMatchMethodIsValidCandidate(targetMethod, candidate))
{
- throw NotImplemented.ActiveIssue("https://github.com/dotnet/corert/issues/190");
+ implMethod = candidate;
+
+ // If reverseMethodSearch is enabled, we want to find the last match on this type, not the first
+ // (reverseMethodSearch is used for most matches except for searches for name/sig method matches for interface methods on the most derived type)
+ if (!reverseMethodSearch)
+ return implMethod;
}
- implMethod = candidate;
}
}
}
@@ -313,7 +324,7 @@ namespace Internal.TypeSystem
{
while (currentType != null)
{
- MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(targetMethod, currentType);
+ MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(targetMethod, currentType, reverseMethodSearch:true);
if (nameSigOverride != null)
{
@@ -339,7 +350,7 @@ namespace Internal.TypeSystem
// Loop until a newslot method is found
while ((currentType != null) && !method.IsNewSlot)
{
- MethodDesc foundMethod = FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType);
+ MethodDesc foundMethod = FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType, reverseMethodSearch: true, nameSigMatchMethodIsValidCandidate:null);
if (foundMethod != null)
{
method = foundMethod;
@@ -353,22 +364,25 @@ namespace Internal.TypeSystem
return method;
}
- private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(MethodDesc method, DefType currentType)
+ /// <summary>
+ /// Find matching a matching method by name and sig on a type. (Restricted to virtual methods only) Only search amongst methods with the same vtable slot.
+ /// </summary>
+ /// <param name="method"></param>
+ /// <param name="currentType"></param>
+ /// <param name="reverseMethodSearch">Used to control the order of the search. For historical purposes to
+ /// match .NET Framework behavior, this is typically true, but not always. There is no particular rationale
+ /// for the particular orders other than to attempt to be consistent in virtual method override behavior
+ /// betweeen runtimes.</param>
+ /// <returns></returns>
+ private static MethodDesc FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(MethodDesc method, DefType currentType, bool reverseMethodSearch)
{
- MethodDesc foundMethod = FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType);
- if (foundMethod != null)
- {
- if (VerifyMethodsHaveTheSameVirtualSlot(foundMethod, method))
- {
- return foundMethod;
- }
- }
-
- return null;
+ return FindMatchingVirtualMethodOnTypeByNameAndSig(method, currentType, reverseMethodSearch, nameSigMatchMethodIsValidCandidate: s_VerifyMethodsHaveTheSameVirtualSlot);
}
+ private static Func<MethodDesc, MethodDesc, bool> s_VerifyMethodsHaveTheSameVirtualSlot = VerifyMethodsHaveTheSameVirtualSlot;
+
// Return true if the slot that defines methodToVerify matches slotDefiningMethod
- private static bool VerifyMethodsHaveTheSameVirtualSlot(MethodDesc methodToVerify, MethodDesc slotDefiningMethod)
+ private static bool VerifyMethodsHaveTheSameVirtualSlot(MethodDesc slotDefiningMethod, MethodDesc methodToVerify)
{
MethodDesc slotDefiningMethodOfMethodToVerify = FindSlotDefiningMethodForVirtualMethod(methodToVerify);
return slotDefiningMethodOfMethodToVerify == slotDefiningMethod;
@@ -384,7 +398,7 @@ namespace Internal.TypeSystem
unificationGroup.SetDefiningMethod(methodImpl);
}
- MethodDesc nameSigMatchMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(unificationGroup.DefiningMethod, currentType);
+ MethodDesc nameSigMatchMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(unificationGroup.DefiningMethod, currentType, reverseMethodSearch: true);
MetadataType baseType = currentType.MetadataBaseType;
// Unless the current type has a name/sig match for the group, look to the base type to define the unification group further
@@ -404,7 +418,7 @@ namespace Internal.TypeSystem
foreach (MethodDesc memberMethod in unificationGroup)
{
- MethodDesc nameSigMatchMemberMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(memberMethod, currentType);
+ MethodDesc nameSigMatchMemberMethod = FindMatchingVirtualMethodOnTypeByNameAndSigWithSlotCheck(memberMethod, currentType, reverseMethodSearch: true);
if (nameSigMatchMemberMethod != null)
{
if (separatedMethods == null)
@@ -504,7 +518,9 @@ namespace Internal.TypeSystem
if (foundExplicitInterface)
{
- MethodDesc foundOnCurrentType = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType);
+ MethodDesc foundOnCurrentType = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType
+ , reverseMethodSearch: false /* When searching for name/sig overrides on a type that explicitly defines an interface, search through the type in the forward direction*/
+ , nameSigMatchMethodIsValidCandidate :null);
foundOnCurrentType = FindSlotDefiningMethodForVirtualMethod(foundOnCurrentType);
if (baseType == null)
@@ -540,7 +556,16 @@ namespace Internal.TypeSystem
}
else
{
- return FindNameSigOverrideForInterfaceMethodRecursive(interfaceMethod, currentType);
+ MethodDesc foundOnCurrentType = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType
+ , reverseMethodSearch: false /* When searching for name/sig overrides on a type that is the first type in the hierarchy to require the interface, search through the type in the forward direction*/
+ , nameSigMatchMethodIsValidCandidate: null);
+
+ foundOnCurrentType = FindSlotDefiningMethodForVirtualMethod(foundOnCurrentType);
+
+ if (foundOnCurrentType != null)
+ return foundOnCurrentType;
+
+ return FindNameSigOverrideForInterfaceMethodRecursive(interfaceMethod, baseType);
}
}
}
@@ -605,7 +630,10 @@ namespace Internal.TypeSystem
if (currentType == null)
return null;
- MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType);
+ MethodDesc nameSigOverride = FindMatchingVirtualMethodOnTypeByNameAndSig(interfaceMethod, currentType
+ , reverseMethodSearch: true /* When searching for a name sig match for an interface on parent types search in reverse order of declaration */
+ , nameSigMatchMethodIsValidCandidate:null);
+
if (nameSigOverride != null)
{
return FindSlotDefiningMethodForVirtualMethod(nameSigOverride);
diff --git a/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs b/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs
index db5912bfa..bbe4eed38 100644
--- a/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs
+++ b/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Metadata;
diff --git a/src/Common/src/TypeSystem/IL/DelegateInfo.cs b/src/Common/src/TypeSystem/IL/DelegateInfo.cs
index 66267bf45..5322fc618 100644
--- a/src/Common/src/TypeSystem/IL/DelegateInfo.cs
+++ b/src/Common/src/TypeSystem/IL/DelegateInfo.cs
@@ -209,7 +209,10 @@ namespace Internal.IL
private static bool IsNativeCallingConventionCompatible(TypeDesc type)
{
- if (type.IsPointer || type.IsByRef)
+ if (type.IsPointer)
+ return true;
+
+ if (type.IsByRef)
return IsNativeCallingConventionCompatible(((ParameterizedType)type).ParameterType);
if (!type.IsValueType)
diff --git a/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs
index 4e8e7a171..e9a431af1 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs
@@ -87,7 +87,7 @@ namespace Internal.IL.Stubs
var rangeExceptionLabel = _emitter.NewCodeLabel();
ILCodeLabel typeMismatchExceptionLabel = null;
- if (!_elementType.IsValueType)
+ if (_elementType.IsGCPointer)
{
// Type check
if (_method.Kind == ArrayMethodKind.Set)
diff --git a/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
index 596a9b4bf..042636f41 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
@@ -549,25 +549,6 @@ namespace Internal.IL.Stubs
}
}
- // Workaround for places that emit IL that doesn't conform to the ECMA-335 CIL stack requirements.
- // https://github.com/dotnet/corert/issues/5152
- // This class and all references to it should be deleted when the issue is fixed.
- // Do not add new references to this.
- public class ILStubMethodILWithNonConformingStack : ILStubMethodIL
- {
- public ILStubMethodILWithNonConformingStack(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo)
- : base(owningMethod, ilBytes, locals, tokens, debugInfo)
- {
- }
-
- public ILStubMethodILWithNonConformingStack(ILStubMethodIL methodIL)
- : base(methodIL)
- {
- }
-
- public override int MaxStack => GetILBytes().Length;
- }
-
public class ILCodeLabel
{
private ILCodeStream _codeStream;
@@ -665,7 +646,7 @@ namespace Internal.IL.Stubs
return newLabel;
}
- public MethodIL Link(MethodDesc owningMethod, bool nonConformingStackWorkaround = false)
+ public MethodIL Link(MethodDesc owningMethod)
{
int totalLength = 0;
int numSequencePoints = 0;
@@ -711,17 +692,8 @@ namespace Internal.IL.Stubs
debugInfo = new EmittedMethodDebugInformation(sequencePoints);
}
- ILStubMethodIL result;
- if (nonConformingStackWorkaround)
- {
- // nonConformingStackWorkaround is a workaround for https://github.com/dotnet/corert/issues/5152
- result = new ILStubMethodILWithNonConformingStack(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
- }
- else
- {
- result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
- result.CheckStackBalance();
- }
+ var result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
+ result.CheckStackBalance();
return result;
}
diff --git a/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
index 6a0669b8c..fa503b742 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
@@ -107,6 +107,7 @@ namespace Internal.IL.Stubs
{
ILEmitter emitter = ilCodeStreams.Emitter;
ILCodeStream fnptrLoadStream = ilCodeStreams.FunctionPointerLoadStream;
+ ILCodeStream marshallingCodeStream = ilCodeStreams.MarshallingCodeStream;
ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream;
TypeSystemContext context = _targetMethod.Context;
@@ -155,7 +156,7 @@ namespace Internal.IL.Stubs
ILLocalVariable vDelegateStub = emitter.NewLocal(delegateMethod.DelegateType);
fnptrLoadStream.EmitStLoc(vDelegateStub);
- fnptrLoadStream.EmitLdLoc(vDelegateStub);
+ marshallingCodeStream.EmitLdLoc(vDelegateStub);
MethodDesc invokeMethod = delegateMethod.DelegateType.GetKnownMethod("Invoke", null);
callsiteSetupCodeStream.Emit(ILOpcode.callvirt, emitter.NewToken(invokeMethod));
}
@@ -304,10 +305,10 @@ namespace Internal.IL.Stubs
EmitPInvokeCall(pInvokeILCodeStreams);
}
+ _marshallers[0].LoadReturnValue(unmarshallingCodestream);
unmarshallingCodestream.Emit(ILOpcode.ret);
- return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod, nonConformingStackWorkaround: true),
- IsStubRequired());
+ return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod), IsStubRequired());
}
public static MethodIL EmitIL(MethodDesc method,
@@ -395,7 +396,7 @@ namespace Internal.IL.Stubs
}
}
- public sealed class PInvokeILStubMethodIL : ILStubMethodILWithNonConformingStack
+ public sealed class PInvokeILStubMethodIL : ILStubMethodIL
{
public bool IsStubRequired { get; }
public PInvokeILStubMethodIL(ILStubMethodIL methodIL, bool isStubRequired) : base(methodIL)
diff --git a/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs b/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs
index 86ce8c7f9..7bc575a77 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs
@@ -52,6 +52,18 @@ namespace Internal.IL.Stubs
(byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq),
(byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
+ case "IsAddressGreaterThan":
+ return new ILStubMethodIL(method, new byte[]
+ {
+ (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
+ (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.cgt_un),
+ (byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
+ case "IsAddressLessThan":
+ return new ILStubMethodIL(method, new byte[]
+ {
+ (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
+ (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.clt_un),
+ (byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
case "ByteOffset":
return new ILStubMethodIL(method, new byte[]
{
diff --git a/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs b/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs
new file mode 100644
index 000000000..4122b8c3c
--- /dev/null
+++ b/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ partial class ValueTypeGetFieldHelperMethodOverride
+ {
+ protected internal override int ClassCode => 2036839816;
+
+ protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
+ {
+ var otherMethod = (ValueTypeGetFieldHelperMethodOverride)other;
+
+ return comparer.Compare(_owningType, otherMethod._owningType);
+ }
+ }
+}
diff --git a/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs b/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
new file mode 100644
index 000000000..33d71794a
--- /dev/null
+++ b/src/Common/src/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ /// <summary>
+ /// Synthetic method override of "int ValueType.__GetFieldHelper(Int32, out EETypePtr)". This method is injected
+ /// into all value types that cannot have their Equals(object) and GetHashCode() methods operate on individual
+ /// bytes. The purpose of the override is to provide access to the value types' fields and their types.
+ /// </summary>
+ public sealed partial class ValueTypeGetFieldHelperMethodOverride : ILStubMethod
+ {
+ private DefType _owningType;
+ private MethodSignature _signature;
+
+ internal ValueTypeGetFieldHelperMethodOverride(DefType owningType)
+ {
+ _owningType = owningType;
+ }
+
+ public override TypeSystemContext Context
+ {
+ get
+ {
+ return _owningType.Context;
+ }
+ }
+
+ public override TypeDesc OwningType
+ {
+ get
+ {
+ return _owningType;
+ }
+ }
+
+ public override MethodSignature Signature
+ {
+ get
+ {
+ if (_signature == null)
+ {
+ TypeSystemContext context = _owningType.Context;
+ TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32);
+ TypeDesc eeTypePtrType = context.SystemModule.GetKnownType("System", "EETypePtr");
+
+ _signature = new MethodSignature(0, 0, int32Type, new[] {
+ int32Type,
+ eeTypePtrType.MakeByRefType()
+ });
+ }
+
+ return _signature;
+ }
+ }
+
+ public override MethodIL EmitIL()
+ {
+ TypeDesc owningType = _owningType.InstantiateAsOpen();
+
+ ILEmitter emitter = new ILEmitter();
+
+ TypeDesc eeTypePtrType = Context.SystemModule.GetKnownType("System", "EETypePtr");
+ MethodDesc eeTypePtrOfMethod = eeTypePtrType.GetKnownMethod("EETypePtrOf", null);
+ ILToken eeTypePtrToken = emitter.NewToken(eeTypePtrType);
+
+ var switchStream = emitter.NewCodeStream();
+ var getFieldStream = emitter.NewCodeStream();
+
+ ArrayBuilder<ILCodeLabel> fieldGetters = new ArrayBuilder<ILCodeLabel>();
+ foreach (FieldDesc field in owningType.GetFields())
+ {
+ if (field.IsStatic)
+ continue;
+
+ ILCodeLabel label = emitter.NewCodeLabel();
+ fieldGetters.Add(label);
+
+ getFieldStream.EmitLabel(label);
+ getFieldStream.EmitLdArg(2);
+
+ // We need something we can instantiate EETypePtrOf over. Also, the classlib
+ // code doesn't handle pointers.
+ TypeDesc boxableFieldType = field.FieldType;
+ if (boxableFieldType.IsPointer || boxableFieldType.IsFunctionPointer)
+ boxableFieldType = Context.GetWellKnownType(WellKnownType.IntPtr);
+
+ MethodDesc ptrOfField = eeTypePtrOfMethod.MakeInstantiatedMethod(boxableFieldType);
+ getFieldStream.Emit(ILOpcode.call, emitter.NewToken(ptrOfField));
+
+ getFieldStream.Emit(ILOpcode.stobj, eeTypePtrToken);
+
+ getFieldStream.EmitLdArg(0);
+ getFieldStream.Emit(ILOpcode.ldflda, emitter.NewToken(field));
+
+ getFieldStream.EmitLdArg(0);
+
+ getFieldStream.Emit(ILOpcode.sub);
+
+ getFieldStream.Emit(ILOpcode.ret);
+ }
+
+ if (fieldGetters.Count > 0)
+ {
+ switchStream.EmitLdArg(1);
+ switchStream.EmitSwitch(fieldGetters.ToArray());
+ }
+
+ switchStream.EmitLdc(fieldGetters.Count);
+
+ switchStream.Emit(ILOpcode.ret);
+
+ return emitter.Link(this);
+ }
+
+ public override Instantiation Instantiation
+ {
+ get
+ {
+ return Instantiation.Empty;
+ }
+ }
+
+ public override bool IsVirtual
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return "__GetFieldHelper";
+ }
+ }
+ }
+}
diff --git a/src/Common/src/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs b/src/Common/src/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs
new file mode 100644
index 000000000..a1487a5ac
--- /dev/null
+++ b/src/Common/src/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs
@@ -0,0 +1,243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+using Internal.IL.Stubs;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace Internal.TypeSystem
+{
+ public abstract partial class TypeSystemContext
+ {
+ private MethodDesc _objectEqualsMethod;
+
+ private class ValueTypeMethodHashtable : LockFreeReaderHashtable<DefType, MethodDesc>
+ {
+ protected override int GetKeyHashCode(DefType key) => key.GetHashCode();
+ protected override int GetValueHashCode(MethodDesc value) => value.OwningType.GetHashCode();
+ protected override bool CompareKeyToValue(DefType key, MethodDesc value) => key == value.OwningType;
+ protected override bool CompareValueToValue(MethodDesc v1, MethodDesc v2) => v1.OwningType == v2.OwningType;
+
+ protected override MethodDesc CreateValueFromKey(DefType key)
+ {
+ return new ValueTypeGetFieldHelperMethodOverride(key);
+ }
+ }
+
+ private ValueTypeMethodHashtable _valueTypeMethodHashtable = new ValueTypeMethodHashtable();
+
+ protected virtual IEnumerable<MethodDesc> GetAllMethodsForValueType(TypeDesc valueType)
+ {
+ TypeDesc valueTypeDefinition = valueType.GetTypeDefinition();
+
+ if (RequiresGetFieldHelperMethod((MetadataType)valueTypeDefinition))
+ {
+ MethodDesc getFieldHelperMethod = _valueTypeMethodHashtable.GetOrCreateValue((DefType)valueTypeDefinition);
+
+ // Check that System.ValueType has the method we're overriding.
+ Debug.Assert(valueTypeDefinition.BaseType.GetMethod(getFieldHelperMethod.Name, null) != null);
+
+ if (valueType != valueTypeDefinition)
+ {
+ yield return GetMethodForInstantiatedType(getFieldHelperMethod, (InstantiatedType)valueType);
+ }
+ else
+ {
+ yield return getFieldHelperMethod;
+ }
+ }
+
+ foreach (MethodDesc method in valueType.GetMethods())
+ yield return method;
+ }
+
+ private bool RequiresGetFieldHelperMethod(MetadataType valueType)
+ {
+ if (_objectEqualsMethod == null)
+ _objectEqualsMethod = GetWellKnownType(WellKnownType.Object).GetMethod("Equals", null);
+
+ // If the classlib doesn't have Object.Equals, we don't need this.
+ if (_objectEqualsMethod == null)
+ return false;
+
+ // Byref-like valuetypes cannot be boxed.
+ if (valueType.IsByRefLike)
+ return false;
+
+ // Enums get their overrides from System.Enum.
+ if (valueType.IsEnum)
+ return false;
+
+ return !_typeStateHashtable.GetOrCreateValue(valueType).CanCompareValueTypeBits;
+ }
+
+ private class TypeState
+ {
+ private enum Flags
+ {
+ CanCompareValueTypeBits = 0x0000_0001,
+ CanCompareValueTypeBitsComputed = 0x0000_0002,
+ }
+
+ private volatile Flags _flags;
+ private readonly TypeStateHashtable _hashtable;
+
+ public TypeDesc Type { get; }
+
+ public bool CanCompareValueTypeBits
+ {
+ get
+ {
+ Flags flags = _flags;
+ if ((flags & Flags.CanCompareValueTypeBitsComputed) == 0)
+ {
+ Debug.Assert(Type.IsValueType);
+ if (ComputeCanCompareValueTypeBits((MetadataType)Type))
+ flags |= Flags.CanCompareValueTypeBits;
+ flags |= Flags.CanCompareValueTypeBitsComputed;
+
+ _flags = flags;
+ }
+ return (flags & Flags.CanCompareValueTypeBits) != 0;
+ }
+ }
+
+ public TypeState(TypeDesc type, TypeStateHashtable hashtable)
+ {
+ Type = type;
+ _hashtable = hashtable;
+ }
+
+ private bool ComputeCanCompareValueTypeBits(MetadataType type)
+ {
+ Debug.Assert(type.IsValueType);
+
+ if (type.ContainsGCPointers)
+ return false;
+
+ if (type.IsGenericDefinition)
+ return false;
+
+ OverlappingFieldTracker overlappingFieldTracker = new OverlappingFieldTracker(type);
+
+ bool result = true;
+ foreach (var field in type.GetFields())
+ {
+ if (field.IsStatic)
+ continue;
+
+ if (!overlappingFieldTracker.TrackField(field))
+ {
+ // This field overlaps with another field - can't compare memory
+ result = false;
+ break;
+ }
+
+ TypeDesc fieldType = field.FieldType;
+ if (fieldType.IsPrimitive || fieldType.IsEnum || fieldType.IsPointer || fieldType.IsFunctionPointer)
+ {
+ TypeFlags category = fieldType.UnderlyingType.Category;
+ if (category == TypeFlags.Single || category == TypeFlags.Double)
+ {
+ // Double/Single have weird behaviors around negative/positive zero
+ result = false;
+ break;
+ }
+ }
+ else
+ {
+ // Would be a suprise if this wasn't a valuetype. We checked ContainsGCPointers above.
+ Debug.Assert(fieldType.IsValueType);
+
+ MethodDesc objectEqualsMethod = fieldType.Context._objectEqualsMethod;
+
+ // If the field overrides Equals, we can't use the fast helper because we need to call the method.
+ if (fieldType.FindVirtualFunctionTargetMethodOnObjectType(objectEqualsMethod).OwningType == fieldType)
+ {
+ result = false;
+ break;
+ }
+
+ if (!_hashtable.GetOrCreateValue((MetadataType)fieldType).CanCompareValueTypeBits)
+ {
+ result = false;
+ break;
+ }
+ }
+ }
+
+ // If there are gaps, we can't memcompare
+ if (result && overlappingFieldTracker.HasGaps)
+ result = false;
+
+ return result;
+ }
+ }
+
+ private class TypeStateHashtable : LockFreeReaderHashtable<TypeDesc, TypeState>
+ {
+ protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
+ protected override int GetValueHashCode(TypeState value) => value.Type.GetHashCode();
+ protected override bool CompareKeyToValue(TypeDesc key, TypeState value) => key == value.Type;
+ protected override bool CompareValueToValue(TypeState v1, TypeState v2) => v1.Type == v2.Type;
+
+ protected override TypeState CreateValueFromKey(TypeDesc key)
+ {
+ return new TypeState(key, this);
+ }
+ }
+ private TypeStateHashtable _typeStateHashtable = new TypeStateHashtable();
+
+ private struct OverlappingFieldTracker
+ {
+ private bool[] _usedBytes;
+
+ public OverlappingFieldTracker(MetadataType type)
+ {
+ _usedBytes = new bool[type.InstanceFieldSize.AsInt];
+ }
+
+ public bool TrackField(FieldDesc field)
+ {
+ int fieldBegin = field.Offset.AsInt;
+
+ TypeDesc fieldType = field.FieldType;
+
+ int fieldEnd;
+ if (fieldType.IsPointer || fieldType.IsFunctionPointer)
+ {
+ fieldEnd = fieldBegin + field.Context.Target.PointerSize;
+ }
+ else
+ {
+ Debug.Assert(fieldType.IsValueType);
+ fieldEnd = fieldBegin + ((DefType)fieldType).InstanceFieldSize.AsInt;
+ }
+
+ for (int i = fieldBegin; i < fieldEnd; i++)
+ {
+ if (_usedBytes[i])
+ return false;
+ _usedBytes[i] = true;
+ }
+
+ return true;
+ }
+
+ public bool HasGaps
+ {
+ get
+ {
+ for (int i = 0; i < _usedBytes.Length; i++)
+ if (!_usedBytes[i])
+ return true;
+
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
index 78fc4d24a..a65a6a3a6 100644
--- a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
+++ b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
@@ -101,6 +101,10 @@ namespace Internal.TypeSystem.Interop
if (importModule == "[MRT]" || importModule == "*")
return false;
+ // Force link time symbol resolution for "__Internal" module for compatibility with Mono
+ if (importModule == "__Internal")
+ return false;
+
if (method.Context.Target.IsWindows)
{
return !importModule.StartsWith("api-ms-win-");
@@ -847,9 +851,8 @@ namespace Internal.TypeSystem.Interop
codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(message));
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(exceptionCtor));
codeStream.Emit(ILOpcode.throw_);
- codeStream.Emit(ILOpcode.ret);
- return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method, nonConformingStackWorkaround: true), true);
+ return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method), isStubRequired: true);
}
}
diff --git a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs
index a8f5078a2..ef75539f1 100644
--- a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs
+++ b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs
@@ -434,7 +434,7 @@ namespace Internal.TypeSystem.Interop
}
}
- public void EmitArgumentMarshallingIL()
+ private void EmitArgumentMarshallingIL()
{
switch (MarshalDirection)
{
@@ -443,7 +443,7 @@ namespace Internal.TypeSystem.Interop
}
}
- public void EmitElementMarshallingIL()
+ private void EmitElementMarshallingIL()
{
switch (MarshalDirection)
{
@@ -452,7 +452,7 @@ namespace Internal.TypeSystem.Interop
}
}
- public void EmitFieldMarshallingIL()
+ private void EmitFieldMarshallingIL()
{
switch (MarshalDirection)
{
@@ -526,14 +526,22 @@ namespace Internal.TypeSystem.Interop
StoreNativeValue(_ilCodeStreams.ReturnValueMarshallingCodeStream);
AllocAndTransformNativeToManaged(_ilCodeStreams.ReturnValueMarshallingCodeStream);
+ }
+
+ public virtual void LoadReturnValue(ILCodeStream codeStream)
+ {
+ Debug.Assert(Return);
- LoadManagedValue(_ilCodeStreams.ReturnValueMarshallingCodeStream);
+ switch (MarshalDirection)
+ {
+ case MarshalDirection.Forward: LoadManagedValue(codeStream); return;
+ case MarshalDirection.Reverse: LoadNativeValue(codeStream); return;
+ }
}
protected virtual void SetupArguments()
{
ILEmitter emitter = _ilCodeStreams.Emitter;
- ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream;
if (MarshalDirection == MarshalDirection.Forward)
{
@@ -560,7 +568,6 @@ namespace Internal.TypeSystem.Interop
protected virtual void SetupArgumentsForElementMarshalling()
{
ILEmitter emitter = _ilCodeStreams.Emitter;
- ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream;
_managedHome = new Home(emitter.NewLocal(ManagedType), ManagedType, isByRef: false);
_nativeHome = new Home(emitter.NewLocal(NativeType), NativeType, isByRef: false);
@@ -569,7 +576,6 @@ namespace Internal.TypeSystem.Interop
protected virtual void SetupArgumentsForFieldMarshalling()
{
ILEmitter emitter = _ilCodeStreams.Emitter;
- ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream;
//
// these are temporary locals for propagating value
@@ -581,7 +587,6 @@ namespace Internal.TypeSystem.Interop
protected virtual void SetupArgumentsForReturnValueMarshalling()
{
ILEmitter emitter = _ilCodeStreams.Emitter;
- ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream;
_managedHome = new Home(emitter.NewLocal(ManagedType), ManagedType, isByRef: false);
_nativeHome = new Home(emitter.NewLocal(NativeType), NativeType, isByRef: false);
@@ -768,8 +773,6 @@ namespace Internal.TypeSystem.Interop
StoreManagedValue(_ilCodeStreams.ReturnValueMarshallingCodeStream);
AllocAndTransformManagedToNative(_ilCodeStreams.ReturnValueMarshallingCodeStream);
-
- LoadNativeValue(_ilCodeStreams.ReturnValueMarshallingCodeStream);
}
protected virtual void EmitMarshalArgumentNativeToManaged()
@@ -902,6 +905,10 @@ namespace Internal.TypeSystem.Interop
protected override void EmitMarshalReturnValueNativeToManaged()
{
}
+ public override void LoadReturnValue(ILCodeStream codeStream)
+ {
+ Debug.Assert(Return);
+ }
}
class BlittableValueMarshaller : Marshaller
@@ -1095,7 +1102,7 @@ namespace Internal.TypeSystem.Interop
codeStream.Emit(ILOpcode.brfalse, lNullArray);
// allocate memory
- // nativeParameter = (byte**)CoTaskMemAllocAndZeroMemory((IntPtr)(checked(manageParameter.Length * sizeof(byte*))));
+ // nativeParameter = (byte**)CoTaskMemAllocAndZeroMemory((IntPtr)(checked(managedParameter.Length * sizeof(byte*))));
// loads the number of elements
EmitElementCount(codeStream, MarshalDirection.Forward);
@@ -1104,10 +1111,6 @@ namespace Internal.TypeSystem.Interop
codeStream.Emit(ILOpcode.sizeof_, emitter.NewToken(nativeElementType));
codeStream.Emit(ILOpcode.mul_ovf);
- codeStream.Emit(ILOpcode.call, emitter.NewToken(
- Context.SystemModule.
- GetKnownType("System", "IntPtr").
- GetKnownMethod("op_Explicit", null)));
codeStream.Emit(ILOpcode.call, emitter.NewToken(
Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemAllocAndZeroMemory")));
@@ -1174,8 +1177,7 @@ namespace Internal.TypeSystem.Interop
codeStream.EmitLdLoc(vIndex);
codeStream.EmitLdLoc(vLength);
- codeStream.Emit(ILOpcode.clt);
- codeStream.Emit(ILOpcode.brtrue, lLoopHeader);
+ codeStream.Emit(ILOpcode.blt, lLoopHeader);
codeStream.EmitLabel(lNullArray);
}
@@ -1243,8 +1245,7 @@ namespace Internal.TypeSystem.Interop
codeStream.EmitLabel(lRangeCheck);
codeStream.EmitLdLoc(vIndex);
codeStream.EmitLdLoc(vLength);
- codeStream.Emit(ILOpcode.clt);
- codeStream.Emit(ILOpcode.brtrue, lLoopHeader);
+ codeStream.Emit(ILOpcode.blt, lLoopHeader);
codeStream.EmitLabel(lNullArray);
}
@@ -1323,8 +1324,7 @@ namespace Internal.TypeSystem.Interop
codeStream.EmitLdLoc(vIndex);
codeStream.EmitLdLoc(vLength);
- codeStream.Emit(ILOpcode.clt);
- codeStream.Emit(ILOpcode.brtrue, lLoopHeader);
+ codeStream.Emit(ILOpcode.blt, lLoopHeader);
}
LoadNativeValue(codeStream);
@@ -1831,7 +1831,7 @@ namespace Internal.TypeSystem.Interop
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(
- Context.GetHelperEntryPoint("InteropHelpers", "GetStubForPInvokeDelegate")));
+ Context.GetHelperEntryPoint("InteropHelpers", "GetFunctionPointerForDelegate")));
StoreNativeValue(codeStream);
}
@@ -1842,7 +1842,7 @@ namespace Internal.TypeSystem.Interop
codeStream.Emit(ILOpcode.ldtoken, _ilCodeStreams.Emitter.NewToken(ManagedType));
codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(
- Context.GetHelperEntryPoint("InteropHelpers", "GetPInvokeDelegateForStub")));
+ Context.GetHelperEntryPoint("InteropHelpers", "GetDelegateForFunctionPointer")));
StoreManagedValue(codeStream);
}
@@ -2042,8 +2042,7 @@ namespace Internal.TypeSystem.Interop
codeStream.EmitLdLoc(vIndex);
codeStream.EmitLdLoc(vLength);
- codeStream.Emit(ILOpcode.clt);
- codeStream.Emit(ILOpcode.brtrue, lLoopHeader);
+ codeStream.Emit(ILOpcode.blt, lLoopHeader);
codeStream.EmitLabel(lDone);
}
@@ -2121,8 +2120,7 @@ namespace Internal.TypeSystem.Interop
codeStream.EmitLdLoc(vIndex);
codeStream.EmitLdLoc(vLength);
- codeStream.Emit(ILOpcode.clt);
- codeStream.Emit(ILOpcode.brtrue, lLoopHeader);
+ codeStream.Emit(ILOpcode.blt, lLoopHeader);
}
}
diff --git a/src/Common/src/TypeSystem/RuntimeDetermined/DefType.RuntimeDetermined.cs b/src/Common/src/TypeSystem/RuntimeDetermined/DefType.RuntimeDetermined.cs
index 58dedeb74..02d1b2523 100644
--- a/src/Common/src/TypeSystem/RuntimeDetermined/DefType.RuntimeDetermined.cs
+++ b/src/Common/src/TypeSystem/RuntimeDetermined/DefType.RuntimeDetermined.cs
@@ -10,6 +10,10 @@ namespace Internal.TypeSystem
{
get
{
+ // Handles situation when shared code refers to uninstantiated generic
+ // type definitions (think: LDTOKEN).
+ // Walking the instantiation would make us assert. This is simply
+ // not a runtime determined type.
if (IsGenericDefinition)
return false;
@@ -80,4 +84,4 @@ namespace Internal.TypeSystem
return this;
}
}
-} \ No newline at end of file
+}
diff --git a/src/Common/src/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/Common/src/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs
index ad83c69f6..8fd47116d 100644
--- a/src/Common/src/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs
+++ b/src/Common/src/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs
@@ -84,6 +84,13 @@ namespace Internal.TypeSystem
if (containingType.IsRuntimeDeterminedSubtype)
return true;
+ // Handles situation when shared code refers to uninstantiated generic
+ // method definitions (think: LDTOKEN).
+ // Walking the instantiation would make us assert. This is simply
+ // not a runtime determined method.
+ if (IsGenericMethodDefinition)
+ return false;
+
foreach (TypeDesc typeArg in Instantiation)
{
if (typeArg.IsRuntimeDeterminedSubtype)
diff --git a/src/Common/test-runtime/XUnit.Runtime.depproj b/src/Common/test-runtime/XUnit.Runtime.depproj
index dae724a68..5f1674de3 100644
--- a/src/Common/test-runtime/XUnit.Runtime.depproj
+++ b/src/Common/test-runtime/XUnit.Runtime.depproj
@@ -4,7 +4,7 @@
<!-- Given that xunit packages bring with them part of the framework, we need to specify a runtime in order to get the assets
This RID value doesn't really matter, since the assets we are copying are not RID specific, so defaulting to Windows here
-->
- <RuntimeIdentifiers>win7-x64</RuntimeIdentifiers>
+ <RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RidSpecificAssets>true</RidSpecificAssets>
<OutputPath>$(RuntimePath)</OutputPath>
diff --git a/src/Framework/Framework-native.depproj b/src/Framework/Framework-native.depproj
index 8719e1c85..cc3093531 100644
--- a/src/Framework/Framework-native.depproj
+++ b/src/Framework/Framework-native.depproj
@@ -9,8 +9,6 @@
<NuGetTargetMoniker>.NETCoreApp,Version=v2.0</NuGetTargetMoniker>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('osx.'))">osx-x64</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">linux-x64</RuntimeIdentifiers>
<RidSpecificAssets>true</RidSpecificAssets>
</PropertyGroup>
diff --git a/src/Framework/Framework-uapaot.depproj b/src/Framework/Framework-uapaot.depproj
index 5ad5937ad..ab8e28d15 100644
--- a/src/Framework/Framework-uapaot.depproj
+++ b/src/Framework/Framework-uapaot.depproj
@@ -24,6 +24,8 @@
<FileToInclude Include="System.Reflection.Primitives" />
<FileToInclude Include="System.Runtime.InteropServices" />
<FileToInclude Include="System.Text.RegularExpressions" />
+ <FileToInclude Include="System.Private.Xml" />
+ <FileToInclude Include="System.Private.Xml.Linq" />
<FileToInclude Include="System.Private.Reflection.Metadata.Ecma335" />
</ItemGroup>
diff --git a/src/Framework/Framework.depproj b/src/Framework/Framework.depproj
index 23f206ea6..e994ff425 100644
--- a/src/Framework/Framework.depproj
+++ b/src/Framework/Framework.depproj
@@ -8,8 +8,6 @@
<NuGetTargetMoniker>.NETCoreApp,Version=v2.1</NuGetTargetMoniker>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('osx.'))">osx-x64</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">linux-x64</RuntimeIdentifiers>
<RidSpecificAssets>true</RidSpecificAssets>
</PropertyGroup>
diff --git a/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs
index ccbeb5afb..3a51c070d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs
@@ -155,7 +155,7 @@ namespace ILCompiler
void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)
{
- // We go over all the types and members that need a runtime artiface present in the
+ // We go over all the types and members that need a runtime artifact present in the
// compiled executable and root it.
const string reason = "Reflection";
diff --git a/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs b/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs
index c0ac54a27..b7f8e4346 100644
--- a/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs
@@ -94,6 +94,10 @@ namespace ILCompiler
private bool ComputeIsBlocked(EcmaType type, ModuleBlockingMode blockingMode)
{
+ // If the type is explicitly blocked, it's always blocked.
+ if (type.HasCustomAttribute("System.Runtime.CompilerServices", "ReflectionBlockedAttribute"))
+ return true;
+
// If no blocking is applied to the module, the type is not blocked
if (blockingMode == ModuleBlockingMode.None)
return false;
diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
index b224f558f..4feb7a582 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
@@ -97,13 +97,20 @@ namespace ILCompiler
private SimpleNameHashtable _simpleNameHashtable = new SimpleNameHashtable();
private SharedGenericsMode _genericsMode;
-
+
public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode)
: base(details)
{
_genericsMode = genericsMode;
_vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
+
+ GenericsConfig = new SharedGenericsConfiguration();
+ }
+
+ public SharedGenericsConfiguration GenericsConfig
+ {
+ get;
}
public IReadOnlyDictionary<string, string> InputFilePaths
@@ -345,6 +352,10 @@ namespace ILCompiler
{
return GetAllMethodsForEnum(type);
}
+ else if (type.IsValueType)
+ {
+ return GetAllMethodsForValueType(type);
+ }
return type.GetMethods();
}
@@ -473,4 +484,32 @@ namespace ILCompiler
Disabled,
CanonicalReferenceTypes,
}
+
+ public class SharedGenericsConfiguration
+ {
+ //
+ // Universal Shared Generics heuristics magic values determined empirically
+ //
+ public long UniversalCanonGVMReflectionRootHeuristic_InstantiationCount { get; }
+ public long UniversalCanonGVMDepthHeuristic_NonCanonDepth { get; }
+ public long UniversalCanonGVMDepthHeuristic_CanonDepth { get; }
+
+ // Controls how many different instantiations of a generic method, or method on generic type
+ // should be allowed before trying to fall back to only supplying USG in the reflection
+ // method table.
+ public long UniversalCanonReflectionMethodRootHeuristic_InstantiationCount { get; }
+
+ public SharedGenericsConfiguration()
+ {
+ UniversalCanonGVMReflectionRootHeuristic_InstantiationCount = 4;
+ UniversalCanonGVMDepthHeuristic_NonCanonDepth = 2;
+ UniversalCanonGVMDepthHeuristic_CanonDepth = 1;
+
+ // Unlike the GVM heuristics which are intended to kick in aggresively
+ // this heuristic exists to make it so that a fair amount of generic
+ // expansion is allowed. Numbers are chosen to allow a fairly large
+ // amount of generic expansion before trimming.
+ UniversalCanonReflectionMethodRootHeuristic_InstantiationCount = 1024;
+ }
+ };
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs b/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs
index 33a13fc2e..86cac8dc0 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs
@@ -115,16 +115,21 @@ namespace ILCompiler
if (mangledName != literal)
{
- if (_sha256 == null)
+ byte[] hash;
+ lock (this)
{
- // Use SHA256 hash here to provide a high degree of uniqueness to symbol names without requiring them to be long
- // This hash function provides an exceedingly high likelihood that no two strings will be given equal symbol names
- // This is not considered used for security purpose; however collisions would be highly unfortunate as they will cause compilation
- // failure.
- _sha256 = SHA256.Create();
- }
+ if (_sha256 == null)
+ {
+ // Use SHA256 hash here to provide a high degree of uniqueness to symbol names without requiring them to be long
+ // This hash function provides an exceedingly high likelihood that no two strings will be given equal symbol names
+ // This is not considered used for security purpose; however collisions would be highly unfortunate as they will cause compilation
+ // failure.
+ _sha256 = SHA256.Create();
+ }
- var hash = _sha256.ComputeHash(GetBytesFromString(literal));
+ hash = _sha256.ComputeHash(GetBytesFromString(literal));
+ }
+
mangledName += "_" + BitConverter.ToString(hash).Replace("-", "");
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs
index 6b298e7e9..4f8c8ba46 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs
@@ -36,7 +36,6 @@ namespace ILCompiler.DependencyAnalysis
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs
index 2955b3d0e..95fa4b07e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs
@@ -37,8 +37,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
index 498e5d36d..2383df231 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
@@ -67,7 +67,7 @@ namespace ILCompiler.DependencyAnalysis
protected override ISymbolNode GetBaseTypeNode(NodeFactory factory)
{
- return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.NormalizedBaseType()) : null;
+ return _type.BaseType != null ? factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(_type.BaseType)) : null;
}
protected override int GCDescSize
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs
index 2ee6b9cf9..0624ee1c6 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs
@@ -36,8 +36,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs
index be47cef80..54acfb2af 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs
@@ -23,7 +23,9 @@ namespace ILCompiler.DependencyAnalysis
if (dependencies == null)
dependencies = new DependencyList();
- dependencies.Add(factory.MaximallyConstructableType(method.OwningType), "Reflection invoke");
+ // The fact we need to exclude Project N is likely a bug in Project N metadata manager
+ if (factory.Target.Abi != TargetAbi.ProjectN)
+ dependencies.Add(factory.MaximallyConstructableType(method.OwningType), "Reflection invoke");
if (factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method)
&& ((factory.Target.Abi != TargetAbi.ProjectN) || ProjectNDependencyBehavior.EnableFullAnalysis || !method.IsCanonicalMethod(CanonicalFormKind.Any)))
@@ -98,7 +100,7 @@ namespace ILCompiler.DependencyAnalysis
factory.InteropStubManager.AddDependeciesDueToPInvoke(ref dependencies, factory, method);
- if (method.IsIntrinsic && factory.Target.Abi != TargetAbi.ProjectN)
+ if (method.IsIntrinsic && factory.Target.Abi != TargetAbi.ProjectN && factory.MetadataManager.SupportsReflection)
{
if (method.OwningType is MetadataType owningType)
{
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
index 4bfd1ea7f..a835bc1f7 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
@@ -102,7 +102,7 @@ namespace ILCompiler.DependencyAnalysis
dependencyList.Add(factory.VTable(closestDefType), "VTable");
- if (closestDefType.HasInstantiation)
+ if (closestDefType.HasInstantiation && factory.MetadataManager.SupportsReflection)
{
TypeDesc canonType = _type.ConvertToCanonForm(CanonicalFormKind.Specific);
TypeDesc canonClosestDefType = closestDefType.ConvertToCanonForm(CanonicalFormKind.Specific);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs
index 5dbf7af84..f1582f1d8 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs
@@ -145,14 +145,17 @@ namespace ILCompiler.DependencyAnalysis
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
- // Root the template for the type. In the future, we may want to control this via type reflectability instead.
- if (_owningMethodOrType is MethodDesc)
+ if (factory.MetadataManager.SupportsReflection)
{
- yield return new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout((MethodDesc)_owningMethodOrType), "Type loader template");
- }
- else
- {
- yield return new DependencyListEntry(factory.NativeLayout.TemplateTypeLayout((TypeDesc) _owningMethodOrType), "Type loader template");
+ // Root the template for the type. In the future, we may want to control this via type reflectability instead.
+ if (_owningMethodOrType is MethodDesc)
+ {
+ yield return new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout((MethodDesc)_owningMethodOrType), "Type loader template");
+ }
+ else
+ {
+ yield return new DependencyListEntry(factory.NativeLayout.TemplateTypeLayout((TypeDesc)_owningMethodOrType), "Type loader template");
+ }
}
if (HasFixedSlots)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
index 53eec543b..dc09feaf8 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -231,7 +231,7 @@ namespace ILCompiler.DependencyAnalysis
if (impl.OwningType == defType && !impl.IsAbstract)
{
MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific);
- yield return new CombinedDependencyListEntry(factory.MethodEntrypoint(canonImpl, _type.IsValueType), factory.VirtualMethodUse(decl.Normalize()), "Virtual method");
+ yield return new CombinedDependencyListEntry(factory.MethodEntrypoint(canonImpl, _type.IsValueType), factory.VirtualMethodUse(decl), "Virtual method");
}
}
@@ -243,7 +243,7 @@ namespace ILCompiler.DependencyAnalysis
// Add conditional dependencies for interface methods the type implements. For example, if the type T implements
// interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's
// possible for any IFoo object to actually be an instance of T.
- foreach (DefType interfaceType in defType.NormalizedRuntimeInterfaces())
+ foreach (DefType interfaceType in defType.RuntimeInterfaces)
{
Debug.Assert(interfaceType.IsInterface);
@@ -277,7 +277,7 @@ namespace ILCompiler.DependencyAnalysis
if (_type.RuntimeInterfaces.Length > 0 && !factory.VTable(closestDefType).HasFixedSlots)
{
- foreach (var implementedInterface in _type.NormalizedRuntimeInterfaces())
+ foreach (var implementedInterface in _type.RuntimeInterfaces)
{
// If the type implements ICastable, the methods are implicitly necessary
if (implementedInterface == factory.ICastableInterface)
@@ -634,6 +634,22 @@ namespace ILCompiler.DependencyAnalysis
}
}
+ protected static TypeDesc GetFullCanonicalTypeForCanonicalType(TypeDesc type)
+ {
+ if (type.IsCanonicalSubtype(CanonicalFormKind.Specific))
+ {
+ return type.ConvertToCanonForm(CanonicalFormKind.Specific);
+ }
+ else if (type.IsCanonicalSubtype(CanonicalFormKind.Universal))
+ {
+ return type.ConvertToCanonForm(CanonicalFormKind.Universal);
+ }
+ else
+ {
+ return type;
+ }
+ }
+
protected virtual ISymbolNode GetBaseTypeNode(NodeFactory factory)
{
return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType) : null;
@@ -681,7 +697,7 @@ namespace ILCompiler.DependencyAnalysis
declType = declType.GetClosestDefType();
templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific);
- var baseType = declType.NormalizedBaseType();
+ var baseType = declType.BaseType;
if (baseType != null)
{
Debug.Assert(templateType.BaseType != null);
@@ -1017,18 +1033,18 @@ namespace ILCompiler.DependencyAnalysis
}
// It must be possible to create an EEType for the base type of this type
- TypeDesc baseType = type.NormalizedBaseType();
+ TypeDesc baseType = type.BaseType;
if (baseType != null)
{
// Make sure EEType can be created for this.
- factory.NecessaryTypeSymbol(baseType);
+ factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(baseType));
}
// We need EETypes for interfaces
- foreach (var intf in type.NormalizedRuntimeInterfaces())
+ foreach (var intf in type.RuntimeInterfaces)
{
// Make sure EEType can be created for this.
- factory.NecessaryTypeSymbol(intf);
+ factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(intf));
}
// Validate classes, structs, enums, interfaces, and delegates
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs
index 57313c7a9..d1dffa4b0 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs
@@ -36,7 +36,6 @@ namespace ILCompiler.DependencyAnalysis
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs
index 870f48998..bb6d87553 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs
@@ -35,7 +35,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs
index 7a92d5dce..5f48eff37 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs
@@ -35,7 +35,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs
index f48ec5dd7..0d9907e9f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs
@@ -34,7 +34,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
@@ -71,4 +70,4 @@ namespace ILCompiler.DependencyAnalysis
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
protected internal override int ClassCode => (int)ObjectNodeOrder.GenericTypesHashtableNode;
}
-} \ No newline at end of file
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs
index 2be294a15..812a2a3fb 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs
@@ -36,7 +36,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
index 7e7e582a4..69e35880d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
@@ -38,7 +38,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
/// <summary>
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
index a406b1be5..e2f6a6535 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
@@ -49,7 +49,7 @@ namespace ILCompiler.DependencyAnalysis
result.Add(factory.InterfaceDispatchMapIndirection(_type), "Interface dispatch map indirection node");
// VTable slots of implemented interfaces are consulted during emission
- foreach (TypeDesc runtimeInterface in _type.NormalizedRuntimeInterfaces())
+ foreach (TypeDesc runtimeInterface in _type.RuntimeInterfaces)
{
result.Add(factory.VTable(runtimeInterface), "Interface for a dispatch map");
}
@@ -61,10 +61,10 @@ namespace ILCompiler.DependencyAnalysis
{
var entryCountReservation = builder.ReserveInt();
int entryCount = 0;
- int interfaceIndex = 0;
- foreach (var interfaceType in _type.NormalizedRuntimeInterfaces())
+ for (int interfaceIndex = 0; interfaceIndex < _type.RuntimeInterfaces.Length; interfaceIndex++)
{
+ var interfaceType = _type.RuntimeInterfaces[interfaceIndex];
Debug.Assert(interfaceType.IsInterface);
IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots;
@@ -80,12 +80,10 @@ namespace ILCompiler.DependencyAnalysis
{
builder.EmitShort(checked((short)interfaceIndex));
builder.EmitShort(checked((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0))));
- builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod.Normalize())));
+ builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod)));
entryCount++;
}
}
-
- interfaceIndex++;
}
builder.EmitInt(entryCountReservation, entryCount);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs
index 3fd60f7bd..4418d2984 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs
@@ -39,7 +39,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
/// <summary>
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs
index 46bb90e09..246e804ce 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs
@@ -35,8 +35,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs
index 8289c2f51..e7bce493e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs
@@ -114,7 +114,7 @@ namespace ILCompiler.DependencyAnalysis
protected override sealed string GetName(NodeFactory factory)
{
- string prefix = "MrtImport " + Ordinal.ToStringInvariant() + " __mrt_";
+ string prefix = "MrtImport " + Ordinal.ToStringInvariant() + " __mrt__";
return prefix + GetNonImportedName(factory.NameMangler);
}
@@ -122,7 +122,7 @@ namespace ILCompiler.DependencyAnalysis
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
- sb.Append("__mrt_").Append(nameMangler.CompilationUnitPrefix).Append(GetNonImportedName(nameMangler));
+ sb.Append("__mrt__").Append(nameMangler.CompilationUnitPrefix).Append(GetNonImportedName(nameMangler));
}
public bool RepresentsIndirectionCell => true;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs
index bed4f3fe4..a73b9edc3 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs
@@ -52,7 +52,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public Section LdTokenInfoSection => _ldTokenInfoSection;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
index 6c1be4884..2c9122d7c 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
@@ -734,34 +734,31 @@ namespace ILCompiler.DependencyAnalysis
_owningMethodOrType = owningMethodOrType;
}
- private GenericContextKind ContextKind
+ private GenericContextKind ContextKind(NodeFactory factory)
{
- get
+ if (_owningMethodOrType is MethodDesc)
{
- if (_owningMethodOrType is MethodDesc)
+ MethodDesc owningMethod = (MethodDesc)_owningMethodOrType;
+ Debug.Assert(owningMethod.HasInstantiation);
+ return GenericContextKind.FromMethodHiddenArg | GenericContextKind.NeedsUSGContext;
+ }
+ else
+ {
+ TypeDesc owningType = (TypeDesc)_owningMethodOrType;
+ if (owningType.IsSzArray || owningType.HasSameTypeDefinition(factory.ArrayOfTClass) || owningType.IsValueType || owningType.IsSealed())
{
- MethodDesc owningMethod = (MethodDesc)_owningMethodOrType;
- Debug.Assert(owningMethod.HasInstantiation);
- return GenericContextKind.FromMethodHiddenArg | GenericContextKind.NeedsUSGContext;
+ return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext;
}
else
{
- TypeDesc owningType = (TypeDesc)_owningMethodOrType;
- if (owningType.IsSzArray || owningType.IsValueType || owningType.IsSealed())
- {
- return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext;
- }
- else
- {
- return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext | GenericContextKind.HasDeclaringType;
- }
+ return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext | GenericContextKind.HasDeclaringType;
}
}
}
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context)
{
- if ((ContextKind & GenericContextKind.HasDeclaringType) != 0)
+ if ((ContextKind(context) & GenericContextKind.HasDeclaringType) != 0)
{
return new DependencyListEntry[]
{
@@ -796,10 +793,10 @@ namespace ILCompiler.DependencyAnalysis
Vertex signature;
- GenericContextKind contextKind = ContextKind;
+ GenericContextKind contextKind = ContextKind(factory);
NativeWriter nativeWriter = GetNativeWriter(factory);
- if ((ContextKind & GenericContextKind.HasDeclaringType) != 0)
+ if ((contextKind & GenericContextKind.HasDeclaringType) != 0)
{
signature = nativeWriter.GetTuple(factory.NativeLayout.TypeSignatureVertex((TypeDesc)_owningMethodOrType).WriteVertex(factory), sequence);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs
index 775c60757..2a72b509b 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs
@@ -24,9 +24,9 @@ namespace ILCompiler.DependencyAnalysis
protected override ISymbolNode GetBaseTypeNode(NodeFactory factory)
{
- return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.NormalizedBaseType()) : null;
+ return _type.BaseType != null ? factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(_type.BaseType)) : null;
}
protected internal override int ClassCode => 1505000724;
}
-}
+} \ No newline at end of file
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs
index a7649e88a..7540667e7 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs
@@ -217,14 +217,14 @@ namespace ILCompiler.DependencyAnalysis
private NodeCache<TypeDesc, GenericLookupResult> _objectAllocators;
- public GenericLookupResult ObjectAlloctor(TypeDesc type)
+ public GenericLookupResult ObjectAllocator(TypeDesc type)
{
return _objectAllocators.GetOrAdd(type);
}
private NodeCache<TypeDesc, GenericLookupResult> _arrayAllocators;
- public GenericLookupResult ArrayAlloctor(TypeDesc type)
+ public GenericLookupResult ArrayAllocator(TypeDesc type)
{
return _arrayAllocators.GetOrAdd(type);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
index 2a25d967b..6bfbf947d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -1056,6 +1056,7 @@ namespace ILCompiler.DependencyAnalysis
InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
MetadataManager.AttachToDependencyGraph(graph);
+ ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol);
}
protected struct MethodKey : IEquatable<MethodKey>
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
index 034d3afe2..b42c3e382 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
@@ -278,6 +278,8 @@ namespace ILCompiler.DependencyAnalysis
case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24:
case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26:
case RelocType.IMAGE_REL_BASED_THUMB_MOV32:
+ case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21:
+ case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
// Do not vacate space for this kind of relocation, because
// the space is embedded in the instruction.
break;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
index dde97273e..25242c877 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
@@ -19,7 +19,7 @@ using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
namespace ILCompiler.DependencyAnalysis
{
/// <summary>
- /// Object writer using https://github.com/dotnet/llilc
+ /// Object writer using src/Native/ObjWriter
/// </summary>
internal class ObjectWriter : IDisposable, ITypesDebugInfoWriter
{
@@ -194,14 +194,16 @@ namespace ILCompiler.DependencyAnalysis
private static extern int EmitSymbolRef(IntPtr objWriter, byte[] symbolName, RelocType relocType, int delta);
public int EmitSymbolRef(Utf8StringBuilder symbolName, RelocType relocType, int delta = 0)
{
- // Workaround for ObjectWriter's lack of support for IMAGE_REL_BASED_RELPTR32
- // https://github.com/dotnet/corert/issues/3278
- if (relocType == RelocType.IMAGE_REL_BASED_RELPTR32)
+ if (_targetPlatform.Architecture != TargetArchitecture.ARMEL && _targetPlatform.Architecture != TargetArchitecture.ARM)
{
- relocType = RelocType.IMAGE_REL_BASED_REL32;
- delta = checked(delta + sizeof(int));
+ // Workaround for ObjectWriter's lack of support for IMAGE_REL_BASED_RELPTR32
+ // https://github.com/dotnet/corert/issues/3278
+ if (relocType == RelocType.IMAGE_REL_BASED_RELPTR32)
+ {
+ relocType = RelocType.IMAGE_REL_BASED_REL32;
+ delta = checked(delta + sizeof(int));
+ }
}
-
return EmitSymbolRef(_nativeObjectWriter, symbolName.Append('\0').UnderlyingArray, relocType, delta);
}
@@ -275,6 +277,40 @@ namespace ILCompiler.DependencyAnalysis
[DllImport(NativeObjectWriterFileName)]
private static extern uint GetCompleteClassTypeIndex(IntPtr objWriter, ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptior, DataFieldDescriptor[] fields);
+ [DllImport(NativeObjectWriterFileName)]
+ private static extern void EmitARMFnStart(IntPtr objWriter);
+ public void EmitARMFnStart()
+ {
+ Debug.Assert(!_frameOpened);
+ EmitARMFnStart(_nativeObjectWriter);
+ _frameOpened = true;
+ }
+
+ [DllImport(NativeObjectWriterFileName)]
+ private static extern void EmitARMFnEnd(IntPtr objWriter);
+ public void EmitARMFnEnd()
+ {
+ Debug.Assert(_frameOpened);
+ EmitARMFnEnd(_nativeObjectWriter);
+ _frameOpened = false;
+ }
+
+ [DllImport(NativeObjectWriterFileName)]
+ private static extern void EmitARMExIdxCode(IntPtr objWriter, int nativeOffset, byte[] blob);
+ public void EmitARMExIdxCode(int nativeOffset, byte[] blob)
+ {
+ Debug.Assert(_frameOpened);
+ EmitARMExIdxCode(_nativeObjectWriter, nativeOffset, blob);
+ }
+
+ [DllImport(NativeObjectWriterFileName)]
+ private static extern void EmitARMExIdxLsda(IntPtr objWriter, byte[] blob);
+ public void EmitARMExIdxLsda(byte[] blob)
+ {
+ Debug.Assert(_frameOpened);
+ EmitARMExIdxLsda(_nativeObjectWriter, blob);
+ }
+
public uint GetClassTypeIndex(ClassTypeDescriptor classTypeDescriptor)
{
return GetClassTypeIndex(_nativeObjectWriter, classTypeDescriptor);
@@ -625,20 +661,32 @@ namespace ILCompiler.DependencyAnalysis
public void EmitCFICodes(int offset)
{
+ bool forArm = (_targetPlatform.Architecture == TargetArchitecture.ARMEL || _targetPlatform.Architecture == TargetArchitecture.ARM);
+
// Emit end the old frame before start a frame.
if (_offsetToCfiEnd.Contains(offset))
{
- EmitCFIEnd(offset);
+ if (forArm)
+ EmitARMFnEnd();
+ else
+ EmitCFIEnd(offset);
}
if (_offsetToCfiStart.Contains(offset))
{
- EmitCFIStart(offset);
+ if (forArm)
+ EmitARMFnStart();
+ else
+ EmitCFIStart(offset);
byte[] blobSymbolName;
if (_offsetToCfiLsdaBlobName.TryGetValue(offset, out blobSymbolName))
{
- EmitCFILsda(blobSymbolName);
+ if (forArm)
+ EmitARMExIdxLsda(blobSymbolName);
+ else
+ EmitCFILsda(blobSymbolName);
+
}
else
{
@@ -653,7 +701,10 @@ namespace ILCompiler.DependencyAnalysis
{
foreach (byte[] cfi in cfis)
{
- EmitCFICode(offset, cfi);
+ if (forArm)
+ EmitARMExIdxCode(offset, cfi);
+ else
+ EmitCFICode(offset, cfi);
}
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
index 090fdf7bb..ddd1479b2 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
@@ -168,6 +168,9 @@ namespace ILCompiler.DependencyAnalysis
public override bool HasConditionalStaticDependencies => true;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
+ if (!factory.MetadataManager.SupportsReflection)
+ return Array.Empty<CombinedDependencyListEntry>();
+
List<CombinedDependencyListEntry> conditionalDependencies = new List<CombinedDependencyListEntry>();
NativeLayoutSavedVertexNode templateLayout;
if (_dictionaryOwner is MethodDesc)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs
index 2664308a8..78d27ef41 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs
@@ -22,6 +22,8 @@ namespace ILCompiler.DependencyAnalysis
public ReflectableMethodNode(MethodDesc method)
{
Debug.Assert(method.IsAbstract || method.IsPInvoke);
+ Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any) ||
+ method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method);
_method = method;
}
@@ -45,4 +47,4 @@ namespace ILCompiler.DependencyAnalysis
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
}
-} \ No newline at end of file
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs
index ddae41d98..93bf59979 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs
@@ -39,8 +39,6 @@ namespace ILCompiler.DependencyAnalysis
public override ObjectNodeSection Section => _externalReferences.Section;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public override bool StaticDependenciesAreComputed => true;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
index b41717a86..73a95d2e5 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
@@ -44,8 +44,6 @@ namespace ILCompiler.DependencyAnalysis
public override ObjectNodeSection Section => _externalReferences.Section;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public override bool StaticDependenciesAreComputed => true;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
index a6b2cf4b9..2b5f45090 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
@@ -39,7 +39,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public static bool NeedsVirtualInvokeInfo(MethodDesc method)
@@ -115,7 +114,7 @@ namespace ILCompiler.DependencyAnalysis
if (!method.HasInstantiation)
{
- MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).Normalize();
+ MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots)
{
dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method");
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs
index 9ff7a7243..cc9dd6a2c 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs
@@ -42,8 +42,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public int Offset => 0;
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
@@ -193,4 +191,4 @@ namespace ILCompiler.DependencyAnalysis
/// </summary>
public int Length { get; }
}
-} \ No newline at end of file
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs
index 053a3112b..4ad4a3b89 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs
@@ -33,8 +33,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public int Offset => 0;
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
@@ -101,4 +99,4 @@ namespace ILCompiler.DependencyAnalysis
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
protected internal override int ClassCode => (int)ObjectNodeOrder.ResourceIndexNode;
}
-} \ No newline at end of file
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs
index 1d4f00f6b..d4d8e06ff 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs
@@ -42,8 +42,10 @@ namespace ILCompiler.DependencyAnalysis
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
string name = WrappedMethodIndirectionCellNode.GetMangledName(nameMangler);
- Debug.Assert(name.StartsWith("__mrt_"));
- sb.Append(name.Substring(6));
+ Debug.Assert(name.StartsWith("__mrt__"));
+ // Add a __imp__ prefix to indicate this is a stub to the debugger
+ sb.Append("__imp__");
+ sb.Append(name.Substring("__mrt__".Length));
}
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs
index 728bad0ec..b1ab09a29 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs
@@ -35,8 +35,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public int Offset => 0;
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs
index 0bfc65a28..de12c6f92 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs
@@ -28,8 +28,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
public int Offset => 0;
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs
index 22122f7ed..14b81d92e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs
@@ -38,7 +38,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool IsShareable => false;
public override ObjectNodeSection Section => _externalReferences.Section;
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
index abeb8ee7f..da3d8b6e9 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
@@ -59,7 +59,6 @@ namespace ILCompiler.DependencyAnalysis.ARM64
{
if (symbol.RepresentsIndirectionCell)
{
- Debug.Assert(false, "The following code to emit an jump stub to an indirection cell is untested. When testing on ARM64 please remove this assert and verify it is correct");
// xip0 register num is 0x10
// ADRP xip0, [symbol (21bit ADRP thing)]
@@ -119,6 +118,5 @@ namespace ILCompiler.DependencyAnalysis.ARM64
{
return i == (int)(sbyte)i;
}
-
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs
index 2932a50e2..c32bcbe70 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs
@@ -111,14 +111,16 @@ namespace ILCompiler.DependencyAnalysis
public IEnumerable<TypeGVMEntryInfo> ScanForGenericVirtualMethodEntries()
{
- foreach (var method in _associatedType.GetMethods())
+ foreach (MethodDesc decl in _associatedType.EnumAllVirtualSlots())
{
- if (!method.IsVirtual || !method.HasInstantiation)
+ // Non-Generic virtual methods are tracked by an orthogonal mechanism.
+ if (!decl.HasInstantiation)
continue;
- MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
- Debug.Assert(slotDecl != null);
- yield return new TypeGVMEntryInfo(slotDecl, method, null);
+ MethodDesc impl = _associatedType.FindVirtualFunctionTargetMethodOnObjectType(decl);
+
+ if (impl.OwningType == _associatedType)
+ yield return new TypeGVMEntryInfo(decl, impl, null);
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs
index ea58dd350..75012d6e8 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs
@@ -37,8 +37,6 @@ namespace ILCompiler.DependencyAnalysis
public override bool StaticDependenciesAreComputed => true;
- public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
-
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs
index fbeb03680..776ca6a2b 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs
@@ -205,6 +205,7 @@ namespace ILCompiler
InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
MetadataManager.AttachToDependencyGraph(graph);
+ ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol);
}
protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
index 956afc879..59777bae9 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
@@ -22,8 +22,6 @@ namespace ILCompiler.DependencyAnalysis
public VTableSliceNode(TypeDesc type)
{
Debug.Assert(!type.IsArray, "Wanted to call GetClosestDefType?");
- Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) ||
- type.ConvertToCanonForm(CanonicalFormKind.Specific) == type);
_type = type;
}
@@ -48,10 +46,9 @@ namespace ILCompiler.DependencyAnalysis
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
- DefType baseType = _type.NormalizedBaseType();
- if (baseType != null)
+ if (_type.HasBaseType)
{
- return new[] { new DependencyListEntry(factory.VTable(baseType), "Base type VTable") };
+ return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") };
}
return null;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
index 9f29f53ce..aec52ca2d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
@@ -30,9 +30,6 @@ namespace ILCompiler.DependencyAnalysis
Debug.Assert(!decl.IsRuntimeDeterminedExactMethod);
Debug.Assert(decl.IsVirtual);
- Debug.Assert(!decl.IsCanonicalMethod(CanonicalFormKind.Any) ||
- decl.GetCanonMethodTarget(CanonicalFormKind.Specific) == decl);
-
// Virtual method use always represents the slot defining method of the virtual.
// Places that might see virtual methods being used through an override need to normalize
// to the slot defining method.
@@ -71,7 +68,9 @@ namespace ILCompiler.DependencyAnalysis
dependencies.Add(new DependencyListEntry(factory.VTable(_decl.OwningType), "VTable of a VirtualMethodUse"));
- factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, factory, _decl);
+ // Do not report things like Foo<object, __Canon>.Frob().
+ if (!_decl.IsCanonicalMethod(CanonicalFormKind.Any) || canonDecl == _decl)
+ factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, factory, _decl);
return dependencies;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs
index 97eed867a..2ae053669 100644
--- a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs
@@ -21,6 +21,11 @@ namespace ILCompiler
{
}
+ public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode)
+ {
+ // We don't attach any metadata blobs.
+ }
+
public override IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata()
{
return Array.Empty<ModuleDesc>();
diff --git a/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs
index 27af8432c..b01a4bb5f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs
@@ -90,26 +90,7 @@ namespace ILCompiler
if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method))
continue;
- MetadataRecord record = transform.HandleQualifiedMethod(typicalMethod);
-
- // As a twist, instantiated generic methods appear as if instantiated over their formals.
- if (typicalMethod.HasInstantiation)
- {
- var methodInst = new MethodInstantiation
- {
- Method = record,
- };
- methodInst.GenericTypeArguments.Capacity = typicalMethod.Instantiation.Length;
- foreach (EcmaGenericParameter typeArgument in typicalMethod.Instantiation)
- {
- var genericParam = new TypeReference
- {
- TypeName = (ConstantStringValue)typeArgument.Name,
- };
- methodInst.GenericTypeArguments.Add(genericParam);
- }
- record = methodInst;
- }
+ MetadataRecord record = CreateStackTraceRecord(transform, method);
stackTraceRecords.Add(new KeyValuePair<MethodDesc, MetadataRecord>(
method,
diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
index ab86d40e2..2c2c6123b 100644
--- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
@@ -15,6 +15,14 @@ using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType;
using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob;
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
+using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord;
+using MemberReference = Internal.Metadata.NativeFormat.Writer.MemberReference;
+using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference;
+using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification;
+using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue;
+using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature;
+using MethodInstantiation = Internal.Metadata.NativeFormat.Writer.MethodInstantiation;
+
namespace ILCompiler
{
/// <summary>
@@ -66,7 +74,7 @@ namespace ILCompiler
return result;
}
- public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode)
+ public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode)
{
var metadataNode = new MetadataNode();
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode, metadataNode, metadataNode.EndSymbol);
@@ -135,16 +143,10 @@ namespace ILCompiler
var defaultConstructorMapNode = new DefaultConstructorMapNode(commonFixupsTableNode);
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DefaultConstructorMap), defaultConstructorMapNode, defaultConstructorMapNode, defaultConstructorMapNode.EndSymbol);
-#if !CORERT
- var stackTraceEmbeddedMetadataNode = new StackTraceEmbeddedMetadataNode();
- header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceEmbeddedMetadata), stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode.EndSymbol);
-#endif
-
var stackTraceMethodMappingNode = new StackTraceMethodMappingNode();
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping), stackTraceMethodMappingNode, stackTraceMethodMappingNode, stackTraceMethodMappingNode.EndSymbol);
// The external references tables should go last
- header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol);
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol);
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode, nativeStaticsTableNode, nativeStaticsTableNode.EndSymbol);
}
@@ -567,7 +569,59 @@ namespace ILCompiler
out List<MetadataMapping<FieldDesc>> fieldMappings,
out List<MetadataMapping<MethodDesc>> stackTraceMapping);
+ protected MetadataRecord CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method)
+ {
+ // In the metadata, we only represent the generic definition
+ MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition();
+ MetadataRecord record = transform.HandleQualifiedMethod(methodToGenerateMetadataFor);
+ // If we're generating a MemberReference to a method on a generic type, the owning type
+ // should appear as if instantiated over its formals
+ TypeDesc owningTypeToGenerateMetadataFor = methodToGenerateMetadataFor.OwningType;
+ if (owningTypeToGenerateMetadataFor.HasInstantiation
+ && record is MemberReference memberRefRecord
+ && memberRefRecord.Parent is TypeReference)
+ {
+ List<MetadataRecord> genericArgs = new List<MetadataRecord>();
+ foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter genericParam in owningTypeToGenerateMetadataFor.Instantiation)
+ {
+ genericArgs.Add(new TypeReference
+ {
+ TypeName = (ConstantStringValue)genericParam.Name,
+ });
+ }
+
+ memberRefRecord.Parent = new TypeSpecification
+ {
+ Signature = new TypeInstantiationSignature
+ {
+ GenericType = memberRefRecord.Parent,
+ GenericTypeArguments = genericArgs,
+ }
+ };
+ }
+
+ // As a twist, instantiated generic methods appear as if instantiated over their formals.
+ if (methodToGenerateMetadataFor.HasInstantiation)
+ {
+ var methodInst = new MethodInstantiation
+ {
+ Method = record,
+ };
+ methodInst.GenericTypeArguments.Capacity = methodToGenerateMetadataFor.Instantiation.Length;
+ foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation)
+ {
+ var genericParam = new TypeReference
+ {
+ TypeName = (ConstantStringValue)typeArgument.Name,
+ };
+ methodInst.GenericTypeArguments.Add(genericParam);
+ }
+ record = methodInst;
+ }
+
+ return record;
+ }
/// <summary>
/// Returns a set of modules that will get some metadata emitted into the output module
diff --git a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
index 6b259d5a1..15070a4b4 100644
--- a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
@@ -113,16 +113,5 @@ namespace ILCompiler
return false;
}
-
- /// <summary>
- /// Gets fully canonicalized method if method is canonical.
- /// </summary>
- public static MethodDesc Normalize(this MethodDesc method)
- {
- // If method is Foo<__Canon,object>::Method, we get Foo<__Canon,__Canon>::Method.
- if (method.IsCanonicalMethod(CanonicalFormKind.Any))
- return method.GetCanonMethodTarget(CanonicalFormKind.Specific);
- return method;
- }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs
index 7e0cad821..0e333d323 100644
--- a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs
@@ -19,6 +19,7 @@ using ILCompiler.Metadata;
using ILCompiler.DependencyAnalysis;
using Debug = System.Diagnostics.Debug;
+using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob;
namespace ILCompiler
{
@@ -74,6 +75,14 @@ namespace ILCompiler
_stackTraceEmissionPolicy = stackTraceEmissionPolicy;
}
+ public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode)
+ {
+ base.AddToReadyToRunHeader(header, nodeFactory, commonFixupsTableNode);
+
+ var stackTraceEmbeddedMetadataNode = new StackTraceEmbeddedMetadataNode();
+ header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceEmbeddedMetadata), stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode.EndSymbol);
+ }
+
/// <summary>
/// Read a method that describes the type system to metadata mappings.
/// </summary>
@@ -135,6 +144,9 @@ namespace ILCompiler
private IEnumerable<TypeSystemEntity> ReadRequiredGenericsEntities(MethodIL method)
{
ILStreamReader il = new ILStreamReader(method);
+ bool needSecondPass = false;
+ Dictionary<MethodDesc, long> openMethodToInstantiationCount = new Dictionary<MethodDesc, long>();
+
// structure is
// REPEAT N TIMES
//ldtoken generic type/method/field
@@ -142,7 +154,7 @@ namespace ILCompiler
while (true)
{
if (il.TryReadRet()) // ret
- yield break;
+ break;
TypeSystemEntity tse;
il.TryReadLdtokenAsTypeSystemEntity(out tse);
@@ -180,12 +192,89 @@ namespace ILCompiler
genericMethod.OwningType.Instantiation.CheckValidInstantiationArguments() &&
genericMethod.CheckConstraints())
{
- // TODO: Detect large number of instantiations of the same method and collapse to using dynamic
- // USG instantiations at runtime, to avoid infinite generic expansion and large compilation times.
- yield return tse;
+ // If we encounter a large number of instantiations of the same generic method, add the universal generic form
+ // and stop adding further instantiations over the same generic method definition
+ if (genericMethod.HasInstantiation || genericMethod.OwningType.HasInstantiation)
+ {
+ MethodDesc openMethod = genericMethod.GetTypicalMethodDefinition();
+ long count;
+ if (openMethodToInstantiationCount.TryGetValue(openMethod, out count))
+ {
+ openMethodToInstantiationCount[openMethod] = count + 1;
+ }
+ else
+ {
+ openMethodToInstantiationCount.Add(openMethod, 1);
+ }
+
+ needSecondPass = true;
+ }
+ else
+ {
+ yield return tse;
+ }
}
}
}
+
+ if (needSecondPass)
+ {
+ ILStreamReader ilpass2 = new ILStreamReader(method);
+
+ while (true)
+ {
+ if (ilpass2.TryReadRet())
+ yield break;
+
+ TypeSystemEntity tse;
+ ilpass2.TryReadLdtokenAsTypeSystemEntity(out tse);
+ ilpass2.ReadPop();
+
+ if (tse == null)
+ throw new BadImageFormatException();
+
+ if (tse is MethodDesc)
+ {
+ MethodDesc genericMethod = (MethodDesc)tse;
+
+ if (genericMethod.Instantiation.CheckValidInstantiationArguments() &&
+ genericMethod.OwningType.Instantiation.CheckValidInstantiationArguments() &&
+ genericMethod.CheckConstraints())
+ {
+ // If we encounter a large number of instantiations of the same generic method, add the universal generic form
+ // and stop adding further instantiations over the same generic method definition
+ if (genericMethod.HasInstantiation || genericMethod.OwningType.HasInstantiation)
+ {
+ MethodDesc openMethod = genericMethod.GetTypicalMethodDefinition();
+ long count;
+ bool found = openMethodToInstantiationCount.TryGetValue(openMethod, out count);
+ Debug.Assert(found);
+
+ // We have 2 heuristics, one for GVMs and one for normal methods that happen to have generics
+ bool isGVM = genericMethod.IsVirtual && genericMethod.HasInstantiation;
+ long heuristicCount = isGVM ? _typeSystemContext.GenericsConfig.UniversalCanonGVMReflectionRootHeuristic_InstantiationCount :
+ _typeSystemContext.GenericsConfig.UniversalCanonReflectionMethodRootHeuristic_InstantiationCount;
+
+ if (count >= heuristicCount)
+ {
+ // We've hit the threshold of instantiations so add the USG form
+ tse = genericMethod.GetCanonMethodTarget(CanonicalFormKind.Universal);
+
+ // Set the instantiation count to -1 as a sentinel value
+ openMethodToInstantiationCount[openMethod] = -1;
+ }
+ else if (count == -1)
+ {
+ // Previously we added the USG form to _SpecifiedGenericMethods, now just skip
+ continue;
+ }
+
+ yield return tse;
+ }
+ }
+ }
+ }
+ }
}
private Instantiation GetUniversalCanonicalInstantiation(int numArgs)
@@ -888,28 +977,7 @@ namespace ILCompiler
if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method))
continue;
- // In the metadata, we only represent the generic definition
- MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition();
- MetadataRecord record = transform.HandleQualifiedMethod(methodToGenerateMetadataFor);
-
- // As a twist, instantiated generic methods appear as if instantiated over their formals.
- if (methodToGenerateMetadataFor.HasInstantiation)
- {
- var methodInst = new MethodInstantiation
- {
- Method = record,
- };
- methodInst.GenericTypeArguments.Capacity = methodToGenerateMetadataFor.Instantiation.Length;
- foreach (EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation)
- {
- var genericParam = new TypeReference
- {
- TypeName = (ConstantStringValue)typeArgument.Name,
- };
- methodInst.GenericTypeArguments.Add(genericParam);
- }
- record = methodInst;
- }
+ MetadataRecord record = CreateStackTraceRecord(transform, method);
stackTraceRecords.Add(new KeyValuePair<MethodDesc, MetadataRecord>(
method,
diff --git a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
index 35f8d8503..5cf56d172 100644
--- a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
@@ -183,74 +183,5 @@ namespace ILCompiler
return false;
}
-
- /// <summary>
- /// Gets a fully canonicalized base type if base type is canonical, or unmodified base type otherwise.
- /// </summary>
- public static DefType NormalizedBaseType(this TypeDesc type)
- {
- // Base type for Foo<__Canon> where Foo is defined as
- // class Foo<T> : Bar<T, string> { }
- // is Bar<__Canon, string>. This method normalizes it to Bar<__Canon, __Canon>.
- DefType baseType = type.BaseType;
- if (baseType != null && baseType.IsCanonicalSubtype(CanonicalFormKind.Any))
- baseType = (DefType)baseType.ConvertToCanonForm(CanonicalFormKind.Specific);
- return baseType;
- }
-
- /// <summary>
- /// Gets an interface list that is fully canonicalized if the interfaces are canonical or the unmodified
- /// interface types otherwise.
- /// </summary>
- public static NormalizedInterfaceList NormalizedRuntimeInterfaces(this TypeDesc type)
- {
- // Interface list for Foo<__Canon> where Foo is defined as
- // class Foo<T> : IFooer<T, object>
- // is IFooer<__Canon, object>. This method normalizes it to IFooer<__Canon, __Canon>.
- return new NormalizedInterfaceList(type);
- }
-
- public struct NormalizedInterfaceList
- {
- private readonly TypeDesc _type;
-
- public NormalizedInterfaceList(TypeDesc type)
- {
- _type = type;
- }
-
- public Enumerator GetEnumerator()
- {
- return new Enumerator(_type);
- }
-
- public struct Enumerator
- {
- private readonly DefType[] _interfaces;
- private int _index;
-
- public Enumerator(TypeDesc type)
- {
- _interfaces = type.RuntimeInterfaces;
- _index = -1;
- }
-
- public DefType Current
- {
- get
- {
- DefType intface = _interfaces[_index];
- if (intface.IsCanonicalSubtype(CanonicalFormKind.Any))
- intface = (DefType)intface.ConvertToCanonForm(CanonicalFormKind.Specific);
- return intface;
- }
- }
-
- public bool MoveNext()
- {
- return ++_index < _interfaces.Length;
- }
- }
- }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs b/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs
index 5943a5ed6..6eaf70a75 100644
--- a/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs
@@ -202,7 +202,12 @@ namespace ILCompiler
if (mangledName != literal)
{
- var hash = _sha256.ComputeHash(GetBytesFromString(literal));
+ byte[] hash;
+ lock (this)
+ {
+ hash = _sha256.ComputeHash(GetBytesFromString(literal));
+ }
+
mangledName += "_" + BitConverter.ToString(hash).Replace("-", "");
}
diff --git a/src/ILCompiler.Compiler/src/Logger.cs b/src/ILCompiler.Compiler/src/Logger.cs
index f50ca450d..69cf48064 100644
--- a/src/ILCompiler.Compiler/src/Logger.cs
+++ b/src/ILCompiler.Compiler/src/Logger.cs
@@ -2,6 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#if SUPPORT_JIT
+extern alias System_Private_CoreLib;
+using TextWriter = System_Private_CoreLib::System.IO.TextWriter;
+#endif
+
using System.IO;
namespace ILCompiler
diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
index be4301303..ef08550b7 100644
--- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
+++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
@@ -316,7 +316,13 @@ namespace ILCompiler.CppCodeGen
public string GetCppFieldName(FieldDesc field)
{
- return _compilation.NameMangler.GetMangledFieldName(field).ToString();
+ string name = _compilation.NameMangler.GetMangledFieldName(field).ToString();
+
+ // TODO: name mangling robustness
+ if (name == "register")
+ name = "_" + name + "_";
+
+ return name;
}
public string GetCppStaticFieldName(FieldDesc field)
@@ -329,7 +335,7 @@ namespace ILCompiler.CppCodeGen
public string SanitizeCppVarName(string varName)
{
// TODO: name mangling robustness
- if (varName == "errno" || varName == "environ" || varName == "template" || varName == "typename") // some names collide with CRT headers
+ if (varName == "errno" || varName == "environ" || varName == "template" || varName == "typename" || varName == "register") // some names collide with CRT headers
return "_" + varName + "_";
return _compilation.NameMangler.SanitizeName(varName);
diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs
index 1b88ec3ea..ae062dad6 100644
--- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs
+++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs
@@ -817,7 +817,9 @@ namespace Internal.IL
private void ImportBreak()
{
- throw new NotImplementedException("Opcode: break");
+ AppendLine();
+ Append("__debug_break()");
+ AppendSemicolon();
}
private void ImportLoadVar(int index, bool argument)
@@ -2382,6 +2384,8 @@ namespace Internal.IL
var index = _stack.Pop();
var arrayPtr = _stack.Pop();
+ // TODO: type check, unless readonly prefix was applied
+
// Range check
AppendLine();
Append("__range_check(");
@@ -2637,7 +2641,7 @@ namespace Internal.IL
private void ImportReadOnlyPrefix()
{
- throw new NotImplementedException();
+ _pendingPrefix |= Prefix.ReadOnly;
}
private void TriggerCctor(TypeDesc type)
diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
index d9de5b3c9..c9b2ef2bf 100644
--- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
+++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
@@ -379,6 +379,12 @@
<Compile Include="..\..\Common\src\TypeSystem\IL\Stubs\StructMarshallingThunk.Sorting.cs">
<Link>IL\Stubs\StructMarshallingThunk.Sorting.cs</Link>
</Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\Stubs\ValueTypeGetFieldHelperMethodOverride.cs">
+ <Link>IL\Stubs\ValueTypeGetFieldHelperMethodOverride.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\Stubs\ValueTypeGetFieldHelperMethodOverride.Sorting.cs">
+ <Link>IL\Stubs\ValueTypeGetFieldHelperMethodOverride.Sorting.cs</Link>
+ </Compile>
<Compile Include="..\..\Common\src\TypeSystem\IL\TypeSystemContext.DelegateInfo.cs">
<Link>IL\TypeSystemContext.DelegateInfo.cs</Link>
</Compile>
@@ -418,6 +424,9 @@
<Compile Include="..\..\Common\src\TypeSystem\IL\TypeSystemContext.EnumMethods.cs">
<Link>IL\TypeSystemContext.EnumMethods.cs</Link>
</Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\TypeSystemContext.ValueTypeMethods.cs">
+ <Link>IL\TypeSystemContext.ValueTypeMethods.cs</Link>
+ </Compile>
<Compile Include="..\..\Common\src\TypeSystem\Interop\IL\InlineArrayType.Sorting.cs">
<Link>TypeSystem\Interop\IL\InlineArrayType.Sorting.cs</Link>
</Compile>
diff --git a/src/ILCompiler.TypeSystem/tests/UniversalGenericFieldLayoutTests.cs b/src/ILCompiler.TypeSystem/tests/UniversalGenericFieldLayoutTests.cs
index 090c59a41..938828c79 100644
--- a/src/ILCompiler.TypeSystem/tests/UniversalGenericFieldLayoutTests.cs
+++ b/src/ILCompiler.TypeSystem/tests/UniversalGenericFieldLayoutTests.cs
@@ -21,7 +21,7 @@ namespace TypeSystemTests
public UniversalGenericFieldLayoutTests()
{
- // Architecure specific tests may use these contexts
+ // Architecture specific tests may use these contexts
_contextX64 = new TestTypeSystemContext(TargetArchitecture.X64);
var systemModuleX64 = _contextX64.CreateModuleForSimpleName("CoreTestAssembly");
_contextX64.SetSystemModule(systemModuleX64);
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
index c218dd440..92c191976 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
@@ -484,20 +484,44 @@ namespace Internal.IL
}
/// <summary>
+ /// Represents the result of a ldftn or ldvirtftn
+ /// </summary>
+ internal class FunctionPointerEntry : ExpressionEntry
+ {
+ /// <summary>
+ /// True if the function pointer was loaded as a virtual function pointer
+ /// </summary>
+ public bool IsVirtual { get; }
+
+ public MethodDesc Method { get; }
+
+ public FunctionPointerEntry(string name, MethodDesc method, LLVMValueRef llvmValue, TypeDesc type, bool isVirtual) : base(StackValueKind.NativeInt, name, llvmValue, type)
+ {
+ Method = method;
+ IsVirtual = isVirtual;
+ }
+
+ public override StackEntry Duplicate()
+ {
+ return new FunctionPointerEntry(Name, Method, RawLLVMValue, Type, IsVirtual);
+ }
+ }
+
+ /// <summary>
/// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation
/// </summary>
internal class LdTokenEntry<T> : ExpressionEntry
{
public T LdToken { get; }
- public LdTokenEntry(StackValueKind kind, string name, T token, TypeDesc type = null) : base(kind, name, default(LLVMValueRef), type)
+ public LdTokenEntry(StackValueKind kind, string name, T token, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type)
{
LdToken = token;
}
public override StackEntry Duplicate()
{
- return new LdTokenEntry<T>(Kind, Name, LdToken, Type);
+ return new LdTokenEntry<T>(Kind, Name, LdToken, RawLLVMValue, Type);
}
protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend)
@@ -546,7 +570,10 @@ namespace Internal.IL
protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend)
{
- return _importer.LoadTemp(LocalIndex, type);
+ LLVMTypeRef origLLVMType = ILImporter.GetLLVMTypeForTypeDesc(Type);
+ LLVMValueRef value = _importer.LoadTemp(LocalIndex, origLLVMType);
+
+ return ILImporter.CastIfNecessary(builder, value, type);
}
public override StackEntry Duplicate()
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
index 71da8d50a..8b0936d1e 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
@@ -13,6 +13,7 @@ using ILCompiler.CodeGen;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem.Ecma;
+using System.Linq;
namespace Internal.IL
{
@@ -44,7 +45,7 @@ namespace Internal.IL
private LLVMBuilderRef _builder;
private readonly LocalVariableDefinition[] _locals;
private List<SpilledExpressionEntry> _spilledExpressions = new List<SpilledExpressionEntry>();
-
+ private int _pointerSize;
private readonly byte[] _ilBytes;
/// <summary>
@@ -52,6 +53,8 @@ namespace Internal.IL
/// </summary>
private EvaluationStack<StackEntry> _stack = new EvaluationStack<StackEntry>(0);
+ LLVMTypeRef _universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false);
+
private class BasicBlock
{
// Common fields
@@ -100,6 +103,7 @@ namespace Internal.IL
}
_llvmFunction = GetOrCreateLLVMFunction(mangledName);
_builder = LLVM.CreateBuilder();
+ _pointerSize = compilation.NodeFactory.Target.PointerSize;
}
public void Import()
@@ -112,15 +116,18 @@ namespace Internal.IL
}
catch
{
+ LLVMBasicBlockRef trapBlock = LLVM.AppendBasicBlock(_llvmFunction, "Trap");
+
// Change the function body to trap
foreach (BasicBlock block in _basicBlocks)
{
if (block != null && block.Block.Pointer != IntPtr.Zero)
{
+ LLVM.ReplaceAllUsesWith(block.Block, trapBlock);
LLVM.DeleteBasicBlock(block.Block);
}
}
- LLVMBasicBlockRef trapBlock = LLVM.AppendBasicBlock(_llvmFunction, "Trap");
+
LLVM.PositionBuilderAtEnd(_builder, trapBlock);
EmitTrapCall();
LLVM.BuildRetVoid(_builder);
@@ -138,11 +145,15 @@ namespace Internal.IL
private void GenerateProlog()
{
+ if (!_methodIL.IsInitLocals)
+ {
+ return;
+ }
+
int totalLocalSize = 0;
foreach(LocalVariableDefinition local in _locals)
{
- int localSize = local.Type.GetElementSize().AsInt;
- totalLocalSize += localSize;
+ totalLocalSize = PadNextOffset(local.Type, totalLocalSize);
}
var sp = LLVM.GetFirstParam(_llvmFunction);
@@ -156,8 +167,7 @@ namespace Internal.IL
private LLVMValueRef CreateLLVMFunction(string mangledName)
{
- LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false);
- return LLVM.AddFunction(Module, mangledName , universalSignature);
+ return LLVM.AddFunction(Module, mangledName , _universalSignature);
}
private LLVMValueRef GetOrCreateLLVMFunction(string mangledName)
@@ -185,8 +195,13 @@ namespace Internal.IL
private void ImportCallMemset(LLVMValueRef targetPointer, byte value, int length)
{
LLVMValueRef objectSizeValue = BuildConstInt32(length);
+ ImportCallMemset(targetPointer, value, objectSizeValue);
+ }
+
+ private void ImportCallMemset (LLVMValueRef targetPointer, byte value, LLVMValueRef length)
+ {
var memsetSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.Int8Type(), LLVM.Int32Type(), LLVM.Int32Type(), LLVM.Int1Type() }, false);
- LLVM.BuildCall(_builder, GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), objectSizeValue, BuildConstInt32(1), BuildConstInt1(0) }, String.Empty);
+ LLVM.BuildCall(_builder, GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), length, BuildConstInt32(1), BuildConstInt1(0) }, String.Empty);
}
private void PushLoadExpression(StackValueKind kind, string name, LLVMValueRef rawLLVMValue, TypeDesc type)
@@ -312,6 +327,11 @@ namespace Internal.IL
private void ImportBreak()
{
+ if (DebugtrapFunction.Pointer == IntPtr.Zero)
+ {
+ DebugtrapFunction = LLVM.AddFunction(Module, "llvm.debugtrap", LLVM.FunctionType(LLVM.VoidType(), Array.Empty<LLVMTypeRef>(), false));
+ }
+ LLVM.BuildCall(_builder, DebugtrapFunction, Array.Empty<LLVMValueRef>(), string.Empty);
}
private void ImportLoadVar(int index, bool argument)
@@ -340,9 +360,10 @@ namespace Internal.IL
internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend)
{
- if (targetType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind && sourceType.IsPrimitive && !sourceType.IsPointer)
+ var underlyingSourceType = sourceType.UnderlyingType;
+ if (targetType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind && underlyingSourceType.IsPrimitive && !underlyingSourceType.IsPointer)
{
- var sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(sourceType);
+ var sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(underlyingSourceType);
var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(sourceLLVMType, 0));
return CastIntValue(builder, LLVM.BuildLoad(builder, typedAddress, "ldvalue"), targetType, signExtend);
}
@@ -379,6 +400,10 @@ namespace Internal.IL
{
return LLVM.BuildSExtOrBitCast(builder, value, type, "SExtOrBitCast");
}
+ else if (type.GetIntTypeWidth() > LLVM.TypeOf(value).GetIntTypeWidth())
+ {
+ return LLVM.BuildZExtOrBitCast(builder, value, type, "ZExtOrBitCast");
+ }
else
{
Debug.Assert(typeKind == LLVMTypeKind.LLVMIntegerTypeKind);
@@ -427,7 +452,7 @@ namespace Internal.IL
}
else
{
- varBase = GetTotalRealLocalOffset();
+ varBase = GetTotalRealLocalOffset() + GetTotalParameterOffset();
GetSpillSizeAndOffsetAtIndex(index, out int localSize, out varOffset);
valueType = GetLLVMTypeForTypeDesc(_spilledExpressions[index].Type);
type = _spilledExpressions[index].Type;
@@ -563,6 +588,15 @@ namespace Internal.IL
{
throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}");
}
+ else if (toStoreKind == LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind)
+ {
+ typedToStore = LLVM.BuildFPExt(builder, source, valueType, "FloatToDouble");
+ }
+
+ else if (toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind)
+ {
+ typedToStore = LLVM.BuildFPTrunc(builder, source, valueType, "DoubleToFloat");
+ }
else if (toStoreKind != valueTypeKind && toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind)
{
throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}");
@@ -651,9 +685,9 @@ namespace Internal.IL
int offset = GetTotalRealLocalOffset();
for (int i = 0; i < _spilledExpressions.Count; i++)
{
- offset += _spilledExpressions[i].Type.GetElementSize().AsInt;
+ offset = PadNextOffset(_spilledExpressions[i].Type, offset);
}
- return offset;
+ return offset.AlignUp(_pointerSize);
}
private int GetTotalRealLocalOffset()
@@ -661,9 +695,9 @@ namespace Internal.IL
int offset = 0;
for (int i = 0; i < _locals.Length; i++)
{
- offset += _locals[i].Type.GetElementSize().AsInt;
+ offset = PadNextOffset(_locals[i].Type, offset);
}
- return offset;
+ return offset.AlignUp(_pointerSize);
}
private int GetTotalParameterOffset()
@@ -671,22 +705,22 @@ namespace Internal.IL
int offset = 0;
for (int i = 0; i < _signature.Length; i++)
{
- offset += _signature[i].GetElementSize().AsInt;
+ offset = PadNextOffset(_signature[i], offset);
}
if (!_signature.IsStatic)
{
// If this is a struct, then it's a pointer on the stack
if (_thisType.IsValueType)
{
- offset += _thisType.Context.Target.PointerSize;
+ offset = PadNextOffset(_thisType.MakeByRefType(), offset);
}
else
{
- offset += _thisType.GetElementSize().AsInt;
+ offset = PadNextOffset(_thisType, offset);
}
}
- return offset;
+ return offset.AlignUp(_pointerSize);
}
private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset)
@@ -694,7 +728,7 @@ namespace Internal.IL
int thisSize = 0;
if (!_signature.IsStatic)
{
- thisSize = _thisType.IsValueType ? _thisType.Context.Target.PointerSize : _thisType.GetElementSize().AsInt;
+ thisSize = _thisType.IsValueType ? _thisType.Context.Target.PointerSize : _thisType.GetElementSize().AsInt.AlignUp(_pointerSize);
if (index == 0)
{
size = thisSize;
@@ -713,7 +747,7 @@ namespace Internal.IL
offset = thisSize;
for (int i = 0; i < index; i++)
{
- offset += _signature[i].GetElementSize().AsInt;
+ offset = PadNextOffset(_signature[i], offset);
}
}
@@ -725,7 +759,7 @@ namespace Internal.IL
offset = 0;
for (int i = 0; i < index; i++)
{
- offset += _locals[i].Type.GetElementSize().AsInt;
+ offset = PadNextOffset(_locals[i].Type, offset);
}
}
@@ -737,10 +771,41 @@ namespace Internal.IL
offset = 0;
for (int i = 0; i < index; i++)
{
- offset += _spilledExpressions[i].Type.GetElementSize().AsInt;
+ offset = PadNextOffset(_spilledExpressions[i].Type, offset);
}
}
+ public int PadNextOffset(TypeDesc type, int atOffset)
+ {
+ var size = type is DefType && type.IsValueType ? ((DefType)type).InstanceFieldSize : type.Context.Target.LayoutPointerSize;
+ return PadOffset(type, atOffset) + size.AsInt;
+ }
+
+ public int PadOffset(TypeDesc type, int atOffset)
+ {
+ var fieldAlignment = type is DefType && type.IsValueType ? ((DefType)type).InstanceFieldAlignment : type.Context.Target.LayoutPointerSize;
+ var alignment = LayoutInt.Min(fieldAlignment, new LayoutInt(ComputePackingSize(type))).AsInt;
+ var padding = (atOffset + (alignment - 1)) & ~(alignment - 1);
+ return padding;
+ }
+
+ private static int ComputePackingSize(TypeDesc type)
+ {
+ if (type is MetadataType)
+ {
+ var metaType = type as MetadataType;
+ var layoutMetadata = metaType.GetClassLayout();
+
+ // If a type contains pointers then the metadata specified packing size is ignored (On desktop this is disqualification from ManagedSequential)
+ if (layoutMetadata.PackingSize == 0 || metaType.ContainsGCPointers)
+ return type.Context.Target.DefaultPackingSize;
+ else
+ return layoutMetadata.PackingSize;
+ }
+ else
+ return type.Context.Target.DefaultPackingSize;
+ }
+
private void ImportAddressOfVar(int index, bool argument)
{
TypeDesc type;
@@ -765,6 +830,25 @@ namespace Internal.IL
private void ImportCasting(ILOpcode opcode, int token)
{
+ TypeDesc type = ResolveTypeToken(token);
+
+ //TODO: call GetCastingHelperNameForType from JitHelper.cs (needs refactoring)
+ string function;
+ bool throwing = opcode == ILOpcode.castclass;
+ if (type.IsArray)
+ function = throwing ? "CheckCastArray" : "IsInstanceOfArray";
+ else if (type.IsInterface)
+ function = throwing ? "CheckCastInterface" : "IsInstanceOfInterface";
+ else
+ function = throwing ? "CheckCastClass" : "IsInstanceOfClass";
+
+ var arguments = new StackEntry[]
+ {
+ _stack.Pop(),
+ new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(type, true), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"))
+ };
+
+ _stack.Push(CallRuntime(_compilation.TypeSystemContext, TypeCast, function, arguments, type));
}
private void ImportLoadNull()
@@ -795,7 +879,7 @@ namespace Internal.IL
}
}
- if (callee.IsPInvoke)
+ if (callee.IsPInvoke || (callee.IsInternalCall && callee.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")))
{
ImportRawPInvoke(callee);
return;
@@ -803,7 +887,8 @@ namespace Internal.IL
if (opcode == ILOpcode.newobj)
{
- if (callee.OwningType.IsString)
+ TypeDesc newType = callee.OwningType;
+ if (newType.IsString)
{
// String constructors actually look like regular method calls
IMethodNode node = _compilation.NodeFactory.StringAllocator(callee);
@@ -813,12 +898,32 @@ namespace Internal.IL
}
else
{
- StackEntry newObjResult = AllocateObject(callee.OwningType);
- //one for the real result and one to be consumed by ctor
if (callee.Signature.Length > _stack.Length) //System.Reflection.MemberFilter.ctor
throw new InvalidProgramException();
- _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length);
- _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length);
+
+ StackEntry newObjResult;
+ if (newType.IsValueType)
+ {
+ // Allocate a slot on the shadow stack for the value type
+ int spillIndex = _spilledExpressions.Count;
+ SpilledExpressionEntry spillEntry = new SpilledExpressionEntry(GetStackValueKind(newType), "newobj" + _currentOffset, newType, spillIndex, this);
+ _spilledExpressions.Add(spillEntry);
+ LLVMValueRef addrOfValueType = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused);
+ AddressExpressionEntry valueTypeByRef = new AddressExpressionEntry(StackValueKind.ByRef, "newobj_slot" + _currentOffset, addrOfValueType, newType.MakeByRefType());
+
+ // The ctor needs a reference to the spill slot, but the
+ // actual value ends up on the stack after the ctor is done
+ _stack.InsertAt(spillEntry, _stack.Top - callee.Signature.Length);
+ _stack.InsertAt(valueTypeByRef, _stack.Top - callee.Signature.Length);
+ }
+ else
+ {
+ newObjResult = AllocateObject(callee.OwningType);
+
+ //one for the real result and one to be consumed by ctor
+ _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length);
+ _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length);
+ }
}
}
@@ -827,17 +932,19 @@ namespace Internal.IL
{
throw new NotImplementedException();
}
- if (opcode == ILOpcode.callvirt && callee.IsAbstract)
- {
- throw new NotImplementedException();
- }
- if (callee.OwningType.IsDelegate)
+ if (opcode == ILOpcode.newobj && callee.OwningType.IsDelegate)
{
- throw new NotImplementedException();
+ FunctionPointerEntry functionPointer = ((FunctionPointerEntry)_stack.Peek());
+ DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(callee.OwningType, functionPointer.Method, functionPointer.IsVirtual);
+ callee = delegateInfo.Constructor.Method;
+ if (callee.Signature.Length == 3)
+ {
+ PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString()));
+ }
}
- HandleCall(callee, opcode);
+ HandleCall(callee, callee.Signature, opcode);
}
private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt)
@@ -850,11 +957,11 @@ namespace Internal.IL
throw new NotImplementedException();
if (!_compilation.HasFixedSlotVTable(callee.OwningType))
- _dependencies.Add(_compilation.NodeFactory.VirtualMethodUse(callee));
+ AddVirtualMethodReference(callee);
//TODO: needs runtime support for DispatchByInterface
if (callee.OwningType.IsInterface)
- throw new NotImplementedException();
+ throw new NotImplementedException("Interface call");
return GetCallableVirtualMethod(thisPointer.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), callee);
}
@@ -863,7 +970,7 @@ namespace Internal.IL
return GetOrCreateLLVMFunction(calleeName);
}
}
-
+
private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method)
{
var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method);
@@ -895,17 +1002,10 @@ namespace Internal.IL
private ExpressionEntry AllocateObject(TypeDesc type)
{
MetadataType metadataType = (MetadataType)type;
- int objectSize = metadataType.InstanceByteCount.AsInt;
- if (metadataType.IsValueType)
- {
- objectSize += type.Context.Target.PointerSize;
- }
-
- LLVMValueRef eeTypePointer = GetEETypeForTypeDesc(type, true);
- var rhpNewFastSig = LLVM.FunctionType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), new LLVMTypeRef[] { LLVM.TypeOf(eeTypePointer) }, false);
- var rhpNewFast = GetOrCreateLLVMFunction("RhpNewFast", rhpNewFastSig);
- LLVMValueRef allocatedMemory = LLVM.BuildCall(_builder, rhpNewFast, new LLVMValueRef[] { eeTypePointer }, "newobj");
- return new ExpressionEntry(StackValueKind.ObjRef, "newobj", allocatedMemory, type);
+ var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(metadataType, true), eeTypeDesc) };
+ //TODO: call GetNewObjectHelperForType from JitHelper.cs (needs refactoring)
+ return CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhNewObject", arguments, type);
}
private static LLVMValueRef BuildConstInt1(int number)
@@ -926,16 +1026,22 @@ namespace Internal.IL
private LLVMValueRef GetEETypeForTypeDesc(TypeDesc target, bool constructed)
{
+ var eeTypePointer = GetEETypePointerForTypeDesc(target, constructed);
+ return LLVM.BuildLoad(_builder, eeTypePointer, "eeTypePtrLoad");
+ }
+
+ private LLVMValueRef GetEETypePointerForTypeDesc(TypeDesc target, bool constructed)
+ {
ISymbolNode node;
if (constructed)
{
- node = _compilation.NodeFactory.ConstructedTypeSymbol(target);
+ node = _compilation.NodeFactory.MaximallyConstructableType(target);
}
else
{
node = _compilation.NodeFactory.NecessaryTypeSymbol(target);
}
- LLVMValueRef eeTypePointer = LoadAddressOfSymbolNode(node);
+ LLVMValueRef eeTypePointer = WebAssemblyObjectWriter.GetSymbolValuePointer(Module, node, _compilation.NameMangler, false);
_dependencies.Add(node);
return eeTypePointer;
@@ -956,12 +1062,70 @@ namespace Internal.IL
switch (method.Name)
{
- // Workaround for not being able to build a WASM version of CoreLib. This method
- // would return the x64 size, which is too large for WASM
- case "get_OffsetToStringData":
- if (metadataType.Name == "RuntimeHelpers" && metadataType.Namespace == "System.Runtime.CompilerServices")
+ case "InitializeArray":
+ if (metadataType.Namespace == "System.Runtime.CompilerServices" && metadataType.Name == "RuntimeHelpers")
+ {
+ StackEntry fieldSlot = _stack.Pop();
+ StackEntry arraySlot = _stack.Pop();
+
+ // TODO: Does fldHandle always come from ldtoken? If not, what to do with other cases?
+ if (!(fieldSlot is LdTokenEntry<FieldDesc> checkedFieldSlot) ||
+ !(_compilation.GetFieldRvaData(checkedFieldSlot.LdToken) is BlobNode fieldNode))
+ throw new InvalidProgramException("Provided field handle is invalid.");
+
+ LLVMValueRef src = LoadAddressOfSymbolNode(fieldNode);
+ _dependencies.Add(fieldNode);
+ int srcLength = fieldNode.GetData(_compilation.NodeFactory, false).Data.Length;
+
+ if (arraySlot.Type.IsSzArray)
+ {
+ // Handle single dimensional arrays (vectors).
+ LLVMValueRef arrayObjPtr = arraySlot.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder);
+
+ var argsType = new LLVMTypeRef[]
+ {
+ LLVM.PointerType(LLVM.Int8Type(), 0),
+ LLVM.PointerType(LLVM.Int8Type(), 0),
+ LLVM.Int32Type(),
+ LLVM.Int32Type(),
+ LLVM.Int1Type()
+ };
+ LLVMValueRef memcpyFunction = GetOrCreateLLVMFunction("llvm.memcpy.p0i8.p0i8.i32", LLVM.FunctionType(LLVM.VoidType(), argsType, false));
+
+ var args = new LLVMValueRef[]
+ {
+ LLVM.BuildGEP(_builder, arrayObjPtr, new LLVMValueRef[] { ArrayBaseSize() }, string.Empty),
+ LLVM.BuildBitCast(_builder, src, LLVM.PointerType(LLVM.Int8Type(), 0), string.Empty),
+ BuildConstInt32(srcLength), // TODO: Handle destination array length to avoid runtime overflow.
+ BuildConstInt32(0), // Assume no alignment
+ BuildConstInt1(0)
+ };
+ LLVM.BuildCall(_builder, memcpyFunction, args, string.Empty);
+ }
+ else if (arraySlot.Type.IsMdArray)
+ {
+ // Handle multidimensional arrays.
+ // TODO: Add support for multidimensional array.
+ throw new NotImplementedException();
+ }
+ else
+ {
+ // Handle object-typed first argument. This include System.Array typed array, and any ill-typed argument.
+ // TODO: Emit runtime type check code on array argument and further memcpy.
+ // TODO: Maybe a new runtime interface for this is better than hand-written code emission?
+ throw new NotImplementedException();
+ }
+
+ return true;
+ }
+ break;
+ case "get_Value":
+ if (metadataType.IsByReferenceOfT)
{
- _stack.Push(new Int32ConstantEntry(8, _method.Context.GetWellKnownType(WellKnownType.Int32)));
+ StackEntry byRefHolder = _stack.Pop();
+
+ TypeDesc byRefType = metadataType.Instantiation[0].MakeByRefType();
+ PushLoadExpression(StackValueKind.ByRef, "byref", byRefHolder.ValueForStackKind(StackValueKind.ByRef, _builder, false), byRefType);
return true;
}
break;
@@ -970,73 +1134,105 @@ namespace Internal.IL
return false;
}
- private void HandleCall(MethodDesc callee, ILOpcode opcode = ILOpcode.call)
- {
- AddMethodReference(callee);
- int offset = GetTotalParameterOffset() + GetTotalLocalOffset() + callee.Signature.ReturnType.GetElementSize().AsInt;
-
- LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction),
- new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) },
- String.Empty);
- var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack");
-
- int returnOffset = GetTotalParameterOffset() + GetTotalLocalOffset();
- var returnAddress = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction),
- new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)returnOffset, LLVMMisc.False) },
- String.Empty);
- var castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), "castreturnaddress");
-
- // argument offset
- uint argOffset = 0;
- int instanceAdjustment = 0;
- if (!callee.Signature.IsStatic)
+ private void HandleCall(MethodDesc callee, MethodSignature signature, ILOpcode opcode = ILOpcode.call, LLVMValueRef calliTarget = default(LLVMValueRef))
+ {
+ var parameterCount = signature.Length + (signature.IsStatic ? 0 : 1);
+ // The last argument is the top of the stack. We need to reverse them and store starting at the first argument
+ StackEntry[] argumentValues = new StackEntry[parameterCount];
+ for (int i = 0; i < argumentValues.Length; i++)
{
- instanceAdjustment = 1;
+ argumentValues[argumentValues.Length - i - 1] = _stack.Pop();
}
+ PushNonNull(HandleCall(callee, signature, argumentValues, opcode, calliTarget));
+ }
- // The last argument is the top of the stack. We need to reverse them and store starting at the first argument
- StackEntry[] argumentValues = new StackEntry[callee.Signature.Length + instanceAdjustment];
+ private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature, StackEntry[] argumentValues, ILOpcode opcode = ILOpcode.call, LLVMValueRef calliTarget = default(LLVMValueRef), TypeDesc forcedReturnType = null)
+ {
+ if (opcode == ILOpcode.callvirt && callee.IsVirtual)
+ {
+ AddVirtualMethodReference(callee);
+ }
+ else if (callee != null)
+ {
+ AddMethodReference(callee);
+ }
+ var pointerSize = _compilation.NodeFactory.Target.PointerSize;
- for(int i = 0; i < argumentValues.Length; i++)
+ LLVMValueRef returnAddress;
+ LLVMValueRef castReturnAddress;
+ TypeDesc returnType = signature.ReturnType;
+ SpilledExpressionEntry returnSlot = null;
+ if (!returnType.IsVoid)
{
- argumentValues[argumentValues.Length - i - 1] = _stack.Pop();
+ int returnIndex = _spilledExpressions.Count;
+ returnSlot = new SpilledExpressionEntry(GetStackValueKind(returnType), callee?.Name + "_return", returnType, returnIndex, this);
+ _spilledExpressions.Add(returnSlot);
+ returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused);
+ castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), callee?.Name + "_castreturn");
+ }
+ else
+ {
+ returnAddress = LLVM.ConstNull(LLVM.PointerType(LLVM.Int8Type(), 0));
+ castReturnAddress = returnAddress;
}
+ int offset = GetTotalParameterOffset() + GetTotalLocalOffset();
+ LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction),
+ new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) },
+ String.Empty);
+ var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack");
+
+ // argument offset
+ int argOffset = 0;
+ var instanceAdjustment = signature.IsStatic ? 0 : 1;
for (int index = 0; index < argumentValues.Length; index++)
{
StackEntry toStore = argumentValues[index];
TypeDesc argType;
- if (index == 0 && !callee.Signature.IsStatic)
+ if (index == 0 && !signature.IsStatic)
{
- if(callee.OwningType.IsValueType)
+ if (opcode == ILOpcode.calli)
+ argType = toStore.Type;
+ else if (callee.OwningType.IsValueType)
argType = callee.OwningType.MakeByRefType();
else
argType = callee.OwningType;
}
else
{
- argType = callee.Signature[index - instanceAdjustment];
+ argType = signature[index - instanceAdjustment];
}
LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType);
- ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, argOffset);
+ ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, (uint)argOffset);
+
+ argOffset = PadNextOffset(argType, argOffset);
+ }
- argOffset += (uint)argType.GetElementSize().AsInt;
+ LLVMValueRef fn;
+ if (opcode == ILOpcode.calli)
+ {
+ fn = calliTarget;
+ }
+ else
+ {
+ fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt);
}
- LLVMValueRef fn = LLVMFunctionForMethod(callee, callee.Signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt);
LLVM.BuildCall(_builder, fn, new LLVMValueRef[] {
castShadowStack,
castReturnAddress}, string.Empty);
- if (!callee.Signature.ReturnType.IsVoid)
+ if (!returnType.IsVoid)
{
- LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(callee.Signature.ReturnType);
- LLVMValueRef returnLLVMPointer = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(returnLLVMType, 0), "castreturnpointer");
- PushLoadExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, returnLLVMPointer, callee.Signature.ReturnType);
+ return returnSlot;
+ }
+ else
+ {
+ return null;
}
}
@@ -1045,39 +1241,70 @@ namespace Internal.IL
_dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(method));
}
+ private void AddVirtualMethodReference(MethodDesc method)
+ {
+ _dependencies.Add(_compilation.NodeFactory.VirtualMethodUse(method));
+ }
+ static Dictionary<string, MethodDesc> _pinvokeMap = new Dictionary<string, MethodDesc>();
private void ImportRawPInvoke(MethodDesc method)
{
- LLVMValueRef nativeFunc = LLVM.GetNamedFunction(Module, method.Name);
+ var arguments = new StackEntry[method.Signature.Length];
+ for(int i = 0; i < arguments.Length; i++)
+ {
+ // Arguments are reversed on the stack
+ // Coerce pointers to the native type
+ arguments[arguments.Length - i - 1] = _stack.Pop();
+ }
+
+ PushNonNull(ImportRawPInvoke(method, arguments));
+ }
+
+ private ExpressionEntry ImportRawPInvoke(MethodDesc method, StackEntry[] arguments, TypeDesc forcedReturnType = null)
+ {
//emscripten dies if this is output because its expected to have i32, i32, i64. But the runtime has defined it as i8*, i8*, i64
if (method.Name == "memmove")
throw new NotImplementedException();
+ string realMethodName = method.Name;
- // Create an import if we haven't already
- if (nativeFunc.Pointer == IntPtr.Zero)
+ if (!method.IsPInvoke && method is TypeSystem.Ecma.EcmaMethod)
{
- // Set up native parameter types
- LLVMTypeRef[] paramTypes = new LLVMTypeRef[method.Signature.Length];
- for (int i = 0; i < paramTypes.Length; i++)
+ realMethodName = ((TypeSystem.Ecma.EcmaMethod)method).GetRuntimeImportName() ?? method.Name;
+ }
+ MethodDesc existantDesc;
+ LLVMValueRef nativeFunc;
+ LLVMValueRef realNativeFunc = LLVM.GetNamedFunction(Module, realMethodName);
+ if (_pinvokeMap.TryGetValue(realMethodName, out existantDesc))
+ {
+ if (existantDesc != method)
{
- paramTypes[i] = GetLLVMTypeForTypeDesc(method.Signature[i]);
+ // Set up native parameter types
+ nativeFunc = MakeExternFunction(method, realMethodName, realNativeFunc);
}
+ else
+ {
+ nativeFunc = realNativeFunc;
+ }
+ }
+ else
+ {
+ _pinvokeMap.Add(realMethodName, method);
+ nativeFunc = realNativeFunc;
+ }
- // Define the full signature
- LLVMTypeRef nativeFuncType = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), paramTypes, LLVMMisc.False);
-
- nativeFunc = LLVM.AddFunction(Module, method.Name, nativeFuncType);
- LLVM.SetLinkage(nativeFunc, LLVMLinkage.LLVMDLLImportLinkage);
+ // Create an import if we haven't already
+ if (nativeFunc.Pointer == IntPtr.Zero)
+ {
+ // Set up native parameter types
+ nativeFunc = MakeExternFunction(method, realMethodName);
}
- LLVMValueRef[] arguments = new LLVMValueRef[method.Signature.Length];
- for(int i = 0; i < arguments.Length; i++)
+ LLVMValueRef[] llvmArguments = new LLVMValueRef[method.Signature.Length];
+ for (int i = 0; i < arguments.Length; i++)
{
- // Arguments are reversed on the stack
- // Coerce pointers to the native type
- TypeDesc signatureType = method.Signature[arguments.Length - i - 1];
- arguments[arguments.Length - i - 1] = _stack.Pop().ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder);
+ TypeDesc signatureType = method.Signature[i];
+ llvmArguments[i] = arguments[i].ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder);
}
// Save the top of the shadow stack in case the callee reverse P/Invokes
@@ -1086,10 +1313,36 @@ namespace Internal.IL
LLVM.GetNamedGlobal(Module, "t_pShadowStackTop"));
// Don't name the return value if the function returns void, it's invalid
- var returnValue = LLVM.BuildCall(_builder, nativeFunc, arguments, !method.Signature.ReturnType.IsVoid ? "call" : string.Empty);
+ var returnValue = LLVM.BuildCall(_builder, nativeFunc, llvmArguments, !method.Signature.ReturnType.IsVoid ? "call" : string.Empty);
- if(!method.Signature.ReturnType.IsVoid)
- PushExpression(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, method.Signature.ReturnType);
+ if (!method.Signature.ReturnType.IsVoid)
+ return new ExpressionEntry(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, forcedReturnType ?? method.Signature.ReturnType);
+ else
+ return null;
+ }
+
+ private LLVMValueRef MakeExternFunction(MethodDesc method, string realMethodName, LLVMValueRef realFunction = default(LLVMValueRef))
+ {
+ LLVMValueRef nativeFunc;
+ LLVMTypeRef[] paramTypes = new LLVMTypeRef[method.Signature.Length];
+ for (int i = 0; i < paramTypes.Length; i++)
+ {
+ paramTypes[i] = GetLLVMTypeForTypeDesc(method.Signature[i]);
+ }
+
+ // Define the full signature
+ LLVMTypeRef nativeFuncType = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), paramTypes, LLVMMisc.False);
+
+ if (realFunction.Pointer == IntPtr.Zero)
+ {
+ nativeFunc = LLVM.AddFunction(Module, realMethodName, nativeFuncType);
+ LLVM.SetLinkage(nativeFunc, LLVMLinkage.LLVMDLLImportLinkage);
+ }
+ else
+ {
+ nativeFunc = LLVM.BuildPointerCast(_builder, realFunction, LLVM.PointerType(nativeFuncType, 0), realMethodName + "__slot__");
+ }
+ return nativeFunc;
}
static LLVMValueRef s_shadowStackTop = default(LLVMValueRef);
@@ -1110,6 +1363,16 @@ namespace Internal.IL
private void EmitNativeToManagedThunk(WebAssemblyCodegenCompilation compilation, MethodDesc method, string nativeName, LLVMValueRef managedFunction)
{
+ if (_pinvokeMap.TryGetValue(nativeName, out MethodDesc existing))
+ {
+ if (existing != method)
+ throw new InvalidProgramException("export and import function were mismatched");
+ }
+ else
+ {
+ _pinvokeMap.Add(nativeName, method);
+ }
+
LLVMTypeRef[] llvmParams = new LLVMTypeRef[method.Signature.Length];
for (int i = 0; i < llvmParams.Length; i++)
{
@@ -1118,6 +1381,7 @@ namespace Internal.IL
LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false);
LLVMValueRef thunkFunc = LLVM.AddFunction(compilation.Module, nativeName, thunkSig);
+
LLVMBasicBlockRef block = LLVM.AppendBasicBlock(thunkFunc, "Block0");
LLVMBuilderRef builder = LLVM.CreateBuilder();
LLVM.PositionBuilderAtEnd(builder, block);
@@ -1129,7 +1393,7 @@ namespace Internal.IL
{
LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i);
LLVM.BuildStore(builder, LLVM.GetParam(thunkFunc, (uint)i), CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0)));
- curOffset += method.Signature[i].GetElementSize().AsInt;
+ curOffset = PadNextOffset(method.Signature[i], curOffset);
}
LLVMValueRef retAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "retAddr");
@@ -1146,10 +1410,35 @@ namespace Internal.IL
private void ImportCalli(int token)
{
+ MethodSignature methodSignature = (MethodSignature)_methodIL.GetObject(token);
+ HandleCall(null, methodSignature, ILOpcode.calli, ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(_universalSignature, 0), _builder));
}
private void ImportLdFtn(int token, ILOpcode opCode)
{
+ MethodDesc method = (MethodDesc)_methodIL.GetObject(token);
+ LLVMValueRef targetLLVMFunction = default(LLVMValueRef);
+ if (opCode == ILOpcode.ldvirtftn)
+ {
+ StackEntry thisPointer = _stack.Pop();
+ if (method.IsVirtual)
+ {
+ targetLLVMFunction = LLVMFunctionForMethod(method, thisPointer, true);
+ AddVirtualMethodReference(method);
+ }
+ }
+ else
+ {
+ AddMethodReference(method);
+ }
+
+ if (targetLLVMFunction.Pointer.Equals(IntPtr.Zero))
+ {
+ targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString());
+ }
+
+ var entry = new FunctionPointerEntry("ldftn", method, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn);
+ _stack.Push(entry);
}
private void ImportLoadInt(long value, StackValueKind kind)
@@ -1339,10 +1628,14 @@ namespace Internal.IL
var pointer = _stack.Pop();
Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry);
var expressionPointer = pointer as ExpressionEntry;
- TypeDesc pointerElementType = pointer.Type.GetParameterType();
- LLVMValueRef rawValue = expressionPointer?.RawLLVMValue ?? LLVM.ConstNull(GetLLVMTypeForTypeDesc(pointerElementType));
+ if(type == null)
+ {
+ type = GetWellKnownType(WellKnownType.Object).MakeByRefType();
+ }
+
+ LLVMValueRef pointerElementType = pointer.ValueAsType(type.MakePointerType(), _builder);
_stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, "ldind",
- rawValue, pointer.Type.GetParameterType()));
+ pointerElementType, type));
}
private void ImportStoreIndirect(int token)
@@ -1399,8 +1692,8 @@ namespace Internal.IL
}
LLVMValueRef result;
- LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false);
- LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false);
+ LLVMValueRef left = op2.ValueForStackKind(kind, _builder, false);
+ LLVMValueRef right = op1.ValueForStackKind(kind, _builder, false);
if (kind == StackValueKind.Float)
{
switch (opcode)
@@ -1528,6 +1821,26 @@ namespace Internal.IL
PushExpression(valueToShift.Kind, "shiftop", result, valueToShift.Type);
}
+ bool TypeNeedsSignExtension(TypeDesc targetType)
+ {
+ var enumCleanTargetType = targetType?.UnderlyingType;
+ if(enumCleanTargetType != null && targetType.IsPrimitive)
+ {
+ if(enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) ||
+ enumCleanTargetType.IsWellKnownType(WellKnownType.UInt16) ||
+ enumCleanTargetType.IsWellKnownType(WellKnownType.UInt32) ||
+ enumCleanTargetType.IsWellKnownType(WellKnownType.UInt64) ||
+ enumCleanTargetType.IsWellKnownType(WellKnownType.UIntPtr))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ return false;
+ }
private void ImportCompareOperation(ILOpcode opcode)
{
var op1 = _stack.Pop();
@@ -1546,8 +1859,8 @@ namespace Internal.IL
}
LLVMValueRef result;
- LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, true);
- LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, true);
+ LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op1.Type));
+ LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op2.Type));
if (kind != StackValueKind.Float)
{
@@ -1603,6 +1916,8 @@ namespace Internal.IL
{
StackEntry value = _stack.Pop();
LLVMValueRef convertedValue;
+ TypeDesc destType = GetWellKnownType(wellKnownType);
+
//conv.u for a pointer should change to a int8*
if (wellKnownType == WellKnownType.UIntPtr)
{
@@ -1612,14 +1927,14 @@ namespace Internal.IL
}
else
{
- convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder);
+ convertedValue = value.ValueAsType(destType, _builder);
}
}
else
{
- convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder);
+ convertedValue = value.ValueAsType(destType, _builder);
}
- PushExpression(value.Kind, "conv", convertedValue, value.Type);
+ PushExpression(GetStackValueKind(destType), "conv", convertedValue, destType);
}
private void ImportUnaryOperation(ILOpcode opCode)
@@ -1682,36 +1997,29 @@ namespace Internal.IL
private void ImportUnbox(int token, ILOpcode opCode)
{
TypeDesc type = ResolveTypeToken(token);
- if (type.IsNullable)
- throw new NotImplementedException();
-
+ LLVMValueRef eeType = GetEETypePointerForTypeDesc(type, true);
+ var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ StackEntry boxedObject = _stack.Pop();
if (opCode == ILOpcode.unbox)
{
- var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder);
- LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData");
- var expressionType = type.MakeByRefType();
- //push the pointer to the data, but it shouldnt be implicitly dereferenced
- PushExpression(GetStackValueKind(expressionType), "unboxed", unboxData, expressionType);
+ if (type.IsNullable)
+ throw new NotImplementedException();
+
+ var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc), boxedObject };
+ PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnbox2", arguments));
}
else //unbox_any
{
Debug.Assert(opCode == ILOpcode.unbox_any);
-
- //TODO: when the runtime is ready switch this to calling the real RhUnboxAny
- //LLVMValueRef eeType = GetEETypeForTypeDesc(type);
- //var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
- //LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(type), "objptr");
- //PushExpression(StackValueKind.ByRef, "objPtr", untypedObjectValue, type.MakePointerType());
- //PushExpression(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc);
- //CallRuntimeExport(_compilation.TypeSystemContext, "RhUnboxAny");
- //PushLoadExpression(GetStackValueKind(type), "unboxed", untypedObjectValue, type);
- //this can be removed once we can call RhUnboxAny
- if (!type.IsValueType)
- throw new NotImplementedException();
-
- var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder);
- LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData");
- PushLoadExpression(GetStackValueKind(type), "unboxed", unboxData, type);
+ LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(type), "objptr");
+ var arguments = new StackEntry[]
+ {
+ boxedObject,
+ new ExpressionEntry(StackValueKind.ByRef, "objPtr", untypedObjectValue, type.MakePointerType()),
+ new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc)
+ };
+ CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnboxAny", arguments);
+ PushLoadExpression(GetStackValueKind(type), "unboxed", untypedObjectValue, type);
}
}
@@ -1736,16 +2044,16 @@ namespace Internal.IL
if (ldtokenValue is TypeDesc)
{
ldtokenKind = WellKnownType.RuntimeTypeHandle;
- PushExpression(StackValueKind.ByRef, "ldtoken", GetEETypeForTypeDesc(ldtokenValue as TypeDesc, false), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"));
+ PushLoadExpression(StackValueKind.ByRef, "ldtoken", GetEETypePointerForTypeDesc(ldtokenValue as TypeDesc, false), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"));
MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle");
AddMethodReference(helper);
- HandleCall(helper);
+ HandleCall(helper, helper.Signature);
name = ldtokenValue.ToString();
}
else if (ldtokenValue is FieldDesc)
{
ldtokenKind = WellKnownType.RuntimeFieldHandle;
- value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind));
+ value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), 0, LLVMMisc.False), GetWellKnownType(ldtokenKind));
_stack.Push(value);
}
else if (ldtokenValue is MethodDesc)
@@ -1760,6 +2068,16 @@ namespace Internal.IL
private void ImportLocalAlloc()
{
+ StackEntry allocSizeEntry = _stack.Pop();
+ LLVMValueRef allocSize = allocSizeEntry.ValueAsInt32(_builder, false);
+ LLVMValueRef allocatedMemory = LLVM.BuildArrayAlloca(_builder, LLVMTypeRef.Int8Type(), allocSize, "localloc" + _currentOffset);
+ LLVM.SetAlignment(allocatedMemory, (uint)_pointerSize);
+ if (_methodIL.IsInitLocals)
+ {
+ ImportCallMemset(allocatedMemory, 0, allocSize);
+ }
+
+ PushExpression(StackValueKind.NativeInt, "localloc" + _currentOffset, allocatedMemory, _compilation.TypeSystemContext.GetPointerType(GetWellKnownType(WellKnownType.Void)));
}
private void ImportEndFilter()
@@ -1781,6 +2099,9 @@ namespace Internal.IL
private void ImportSizeOf(int token)
{
+ TypeDesc type = (TypeDesc)_methodIL.GetObject(token);
+ int size = type.GetElementSize().AsInt;
+ PushExpression(StackValueKind.Int32, "sizeof", LLVM.ConstInt(LLVM.Int32Type(), (ulong)size, LLVMMisc.False), GetWellKnownType(WellKnownType.Int32));
}
private void ImportRefAnyType()
@@ -1844,6 +2165,7 @@ namespace Internal.IL
{
untypedObjectValue = objectEntry.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder);
}
+
if (field.Offset.AsInt == 0)
{
return untypedObjectValue;
@@ -1933,17 +2255,19 @@ namespace Internal.IL
private void ImportBox(int token)
{
TypeDesc type = ResolveTypeToken(token);
+ LLVMValueRef eeType = GetEETypePointerForTypeDesc(type, true);
+ var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ var valueAddress = TakeAddressOf(_stack.Pop());
+ var eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", eeType, eeTypeDesc.MakePointerType());
if (type.IsValueType)
{
- if (type.IsNullable)
- throw new NotImplementedException();
-
- var value = _stack.Pop();
- ExpressionEntry boxTarget = AllocateObject(type);
- LLVMValueRef boxData = LLVM.BuildGEP(_builder, boxTarget.RawLLVMValue, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "boxData");
- LLVMValueRef typedBoxData = LLVM.BuildPointerCast(_builder, boxData, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0), "typedBoxData");
- LLVM.BuildStore(_builder, value.ValueAsType(type, _builder), typedBoxData);
- _stack.Push(boxTarget);
+ var arguments = new StackEntry[] { eeTypeEntry, valueAddress };
+ PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBox", arguments));
+ }
+ else
+ {
+ var arguments = new StackEntry[] { valueAddress, eeTypeEntry };
+ PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBoxAny", arguments));
}
}
@@ -1972,30 +2296,78 @@ namespace Internal.IL
private void ImportNewArray(int token)
{
+ TypeDesc arrayType = ResolveTypeToken(token).MakeArrayType();
+ var sizeOfArray = _stack.Pop();
+ var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType();
+ var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(arrayType, true), eeTypeDesc), sizeOfArray };
+ //TODO: call GetNewArrayHelperForType from JitHelper.cs (needs refactoring)
+ PushNonNull(CallRuntime(_compilation.TypeSystemContext, InternalCalls, "RhpNewArray", arguments, arrayType));
+ }
+
+ private LLVMValueRef ArrayBaseSize()
+ {
+ return BuildConstInt32(2 * _compilation.NodeFactory.Target.PointerSize);
}
private void ImportLoadElement(int token)
{
+ ImportLoadElement(ResolveTypeToken(token));
}
private void ImportLoadElement(TypeDesc elementType)
{
+ StackEntry index = _stack.Pop();
+ StackEntry arrayReference = _stack.Pop();
+ var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object);
+ PushLoadExpression(GetStackValueKind(nullSafeElementType), "ldelem", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), nullSafeElementType), nullSafeElementType);
}
private void ImportStoreElement(int token)
{
+ ImportStoreElement(ResolveTypeToken(token));
}
private void ImportStoreElement(TypeDesc elementType)
{
+ StackEntry value = _stack.Pop();
+ StackEntry index = _stack.Pop();
+ StackEntry arrayReference = _stack.Pop();
+ var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object);
+ LLVMValueRef elementAddress = GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), nullSafeElementType);
+ CastingStore(elementAddress, value, nullSafeElementType);
}
private void ImportLoadLength()
{
+ StackEntry arrayReference = _stack.Pop();
+ LLVMValueRef lengthPtr = LLVM.BuildGEP(_builder, arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), new LLVMValueRef[] { BuildConstInt32(_compilation.NodeFactory.Target.PointerSize) }, "arrayLength");
+ LLVMValueRef castLengthPtr = LLVM.BuildPointerCast(_builder, lengthPtr, LLVM.PointerType(LLVM.Int32Type(), 0), "castArrayLength");
+ PushLoadExpression(StackValueKind.Int32, "arrayLength", castLengthPtr, GetWellKnownType(WellKnownType.Int32));
}
private void ImportAddressOfElement(int token)
{
+ TypeDesc elementType = ResolveTypeToken(token);
+ var byRefElement = elementType.MakeByRefType();
+ StackEntry index = _stack.Pop();
+ StackEntry arrayReference = _stack.Pop();
+
+ PushExpression(GetStackValueKind(byRefElement), "ldelema", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), elementType), byRefElement);
+ }
+
+ private LLVMValueRef GetElementAddress(LLVMValueRef elementPosition, LLVMValueRef arrayReference, TypeDesc arrayElementType)
+ {
+ var elementSize = arrayElementType.IsValueType ? ((DefType)arrayElementType).InstanceByteCount : arrayElementType.GetElementSize();
+ LLVMValueRef elementOffset = LLVM.BuildMul(_builder, elementPosition, BuildConstInt32(elementSize.AsInt), "elementOffset");
+ LLVMValueRef arrayOffset = LLVM.BuildAdd(_builder, elementOffset, ArrayBaseSize(), "arrayOffset");
+ return LLVM.BuildGEP(_builder, arrayReference, new LLVMValueRef[] { arrayOffset }, "elementPointer");
+ }
+
+ LLVMValueRef EmitRuntimeHelperCall(string name, TypeDesc returnType, LLVMValueRef[] parameters)
+ {
+ var runtimeHelperSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(returnType), parameters.Select(valRef => LLVM.TypeOf(valRef)).ToArray(), false);
+ var runtimeHelper = GetOrCreateLLVMFunction(name, runtimeHelperSig);
+ return LLVM.BuildCall(_builder, runtimeHelper, parameters, "call_" + name);
}
private void ImportEndFinally()
@@ -2057,11 +2429,26 @@ namespace Internal.IL
}
- private void CallRuntimeExport(TypeSystemContext context, string methodName)
+ private const string RuntimeExport = "RuntimeExports";
+ private const string RuntimeImport = "RuntimeImports";
+ private const string InternalCalls = "InternalCalls";
+ private const string TypeCast = "TypeCast";
+ private ExpressionEntry CallRuntime(TypeSystemContext context, string className, string methodName, StackEntry[] arguments, TypeDesc forcedReturnType = null)
{
- MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", "RuntimeExports");
+ MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", className);
MethodDesc helperMethod = helperType.GetKnownMethod(methodName, null);
- HandleCall(helperMethod);
+ if((helperMethod.IsInternalCall && helperMethod.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")))
+ return ImportRawPInvoke(helperMethod, arguments, forcedReturnType: forcedReturnType);
+ else
+ return HandleCall(helperMethod, helperMethod.Signature, arguments, forcedReturnType: forcedReturnType);
+ }
+
+ private void PushNonNull(StackEntry entry)
+ {
+ if (entry != null)
+ {
+ _stack.Push(entry);
+ }
}
private StackEntry NewSpillSlot(StackEntry entry)
@@ -2078,6 +2465,39 @@ namespace Internal.IL
}
}
+ private StackEntry TakeAddressOf(StackEntry entry)
+ {
+ var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal
+
+ LLVMValueRef addressValue;
+ if(entry is LoadExpressionEntry)
+ {
+ addressValue = ((LoadExpressionEntry)entry).RawLLVMValue;
+ }
+ else if (entry is SpilledExpressionEntry)
+ {
+ int spillIndex = ((SpilledExpressionEntry)entry).LocalIndex;
+ addressValue = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused);
+ }
+ else
+ {
+ //This path should only ever be taken for constants and the results of a primitive cast (not writable)
+ //all other cases should be operating on a LoadExpressionEntry
+ var entryIndex = _spilledExpressions.Count;
+ var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "address_of_temp" + entryIndex, entryType, entryIndex, this);
+ _spilledExpressions.Add(newEntry);
+
+ if (entry is ExpressionEntry)
+ StoreTemp(entryIndex, ((ExpressionEntry)entry).RawLLVMValue);
+ else
+ StoreTemp(entryIndex, entry.ValueForStackKind(entry.Kind, _builder, false));
+
+ addressValue = LoadVarAddress(entryIndex, LocalVarKind.Temp, out TypeDesc type);
+ }
+
+ return new AddressExpressionEntry(StackValueKind.NativeInt, "address_of", addressValue, entry.Type.MakePointerType());
+ }
+
private TypeDesc ResolveTypeToken(int token)
{
return (TypeDesc)_methodIL.GetObject(token);
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
index 002e5e916..ad6bc1c78 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
@@ -100,6 +100,7 @@ namespace Internal.IL
methodCodeNodeNeedingCode.SetDependencies(ilImporter.GetDependencies());
}
+ static LLVMValueRef DebugtrapFunction = default(LLVMValueRef);
static LLVMValueRef TrapFunction = default(LLVMValueRef);
static LLVMValueRef DoNothingFunction = default(LLVMValueRef);
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
index 9cb9b26d5..8808a2e02 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
@@ -183,9 +183,9 @@ namespace ILCompiler.DependencyAnalysis
EmitNativeMain();
LLVM.WriteBitcodeToFile(Module, _objectFilePath);
#if DEBUG
- LLVM.PrintModuleToFile(Module, Path.ChangeExtension(_objectFilePath, ".txt"), out IntPtr unused2);
+ LLVM.PrintModuleToFile(Module, Path.ChangeExtension(_objectFilePath, ".txt"), out string unused2);
#endif //DEBUG
- LLVM.VerifyModule(Module, LLVMVerifierFailureAction.LLVMAbortProcessAction, out IntPtr unused);
+ LLVM.VerifyModule(Module, LLVMVerifierFailureAction.LLVMAbortProcessAction, out string unused);
//throw new NotImplementedException(); // This function isn't complete
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
index 94b35a3f4..5a32248a7 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
@@ -26,7 +26,6 @@ namespace ILCompiler
: base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, null, logger)
{
NodeFactory = nodeFactory;
- LLVM.LoadLibrary_libLLVM("./libLLVM-x64.dll");
Module = LLVM.ModuleCreateWithName("netscripten");
LLVM.SetTarget(Module, "asmjs-unknown-emscripten");
Options = options;
diff --git a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
index cd88923c7..58b4c14eb 100644
--- a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
+++ b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
@@ -10,14 +10,13 @@
<NoWarn>8002</NoWarn>
<OutputPath>$(BaseOutputPath)$(OSPlatformConfig)/tools</OutputPath>
</PropertyGroup>
+
<ItemGroup>
<PackageReference Include="LLVMSharp">
- <Version>3.9.1-rc3</Version>
- </PackageReference>
- <PackageReference Include="libLLVM">
- <Version>3.9.1</Version>
- </PackageReference>
+ <Version>5.0.0</Version>
+ </PackageReference>
</ItemGroup>
+
<ItemGroup Condition="'$(IsProjectNLibrary)' != 'true'">
<ProjectReference Include="..\..\ILCompiler.DependencyAnalysisFramework\src\ILCompiler.DependencyAnalysisFramework.csproj" />
<ProjectReference Include="..\..\ILCompiler.MetadataTransform\src\ILCompiler.MetadataTransform.csproj" />
@@ -52,11 +51,18 @@
<Compile Include="CodeGen\NodeDataSection.cs" />
<Compile Include="CodeGen\ILToWebAssemblyImporter.cs" />
</ItemGroup>
+
<ItemGroup>
- <Content Include="..\..\..\packages\llvmsharp\3.9.1-rc3\lib\netstandard1.1\LLVMSharp.dll">
+ <Content Include="..\..\..\packages\llvmsharp\5.0.0\lib\netstandard1.1\LLVMSharp.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="..\..\..\packages\libllvm\3.9.1\content\libLLVM-x64.dll">
+ <Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\osx\native\libLLVM.dylib" Condition="'$(NuPkgRid)' == 'osx-x64'">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\linux-x64\native\libLLVM.so" Condition="'$(NuPkgRid)' == 'linux-x64'">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\win-x64\native\libLLVM.dll" Condition="'$(NuPkgRid)' == 'win-x64' or '$(NuPkgRid)' == ''">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
diff --git a/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj b/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj
new file mode 100644
index 000000000..5c1bac3da
--- /dev/null
+++ b/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj
@@ -0,0 +1,21 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+
+ <PropertyGroup>
+ <OutputPath>$(BaseOutputPath)$(OSPlatformConfig)/tools</OutputPath>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netstandard1.3</TargetFramework>
+ <RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
+ <RidSpecificAssets>true</RidSpecificAssets>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="libLLVM">
+ <Version>4.0.0</Version>
+ </PackageReference>
+ </ItemGroup>
+
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/ILCompiler/ILCompiler.sln b/src/ILCompiler/ILCompiler.sln
index c210088aa..b8f37a7b3 100644
--- a/src/ILCompiler/ILCompiler.sln
+++ b/src/ILCompiler/ILCompiler.sln
@@ -38,11 +38,15 @@ Global
Debug|Any CPU = Debug|Any CPU
Debug|arm = Debug|arm
Debug|arm64 = Debug|arm64
+ Debug|armel = Debug|armel
+ Debug|wasm = Debug|wasm
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|arm = Release|arm
Release|arm64 = Release|arm64
+ Release|armel = Release|armel
+ Release|wasm = Release|wasm
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
@@ -52,25 +56,37 @@ Global
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm.Build.0 = Debug|arm
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm64.ActiveCfg = Debug|arm64
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm64.Build.0 = Debug|arm64
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|armel.Build.0 = Debug|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|wasm.Build.0 = Debug|Any CPU
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.ActiveCfg = Debug|x64
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.Build.0 = Debug|x64
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x86.ActiveCfg = Debug|x86
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x86.Build.0 = Debug|x86
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|Any CPU.Build.0 = Release|Any CPU
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.ActiveCfg = Debug|arm
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.Build.0 = Debug|arm
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.ActiveCfg = Debug|arm64
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.Build.0 = Debug|arm64
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.ActiveCfg = Debug|x64
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.Build.0 = Debug|x64
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.ActiveCfg = Debug|x86
- {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.Build.0 = Debug|x86
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.ActiveCfg = Release|arm
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.Build.0 = Release|arm
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.ActiveCfg = Release|arm64
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.Build.0 = Release|arm64
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|armel.ActiveCfg = Release|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|armel.Build.0 = Release|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|wasm.ActiveCfg = Release|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|wasm.Build.0 = Release|Any CPU
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.ActiveCfg = Release|x64
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.Build.0 = Release|x64
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.ActiveCfg = Release|x86
+ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.Build.0 = Release|x86
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|Any CPU.ActiveCfg = Debug|x86
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm.ActiveCfg = Debug|arm
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm.Build.0 = Debug|arm
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm64.ActiveCfg = Debug|arm64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm64.Build.0 = Debug|arm64
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|armel.Build.0 = Debug|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|wasm.Build.0 = Debug|Any CPU
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.ActiveCfg = Debug|x64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.Build.0 = Debug|x64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x86.ActiveCfg = Debug|x86
@@ -80,6 +96,10 @@ Global
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm.Build.0 = Release|arm
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm64.ActiveCfg = Release|arm64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm64.Build.0 = Release|arm64
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|armel.ActiveCfg = Release|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|armel.Build.0 = Release|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|wasm.ActiveCfg = Release|Any CPU
+ {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|wasm.Build.0 = Release|Any CPU
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.ActiveCfg = Release|x64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.Build.0 = Release|x64
{FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x86.ActiveCfg = Release|x86
@@ -89,6 +109,10 @@ Global
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm.Build.0 = Debug|arm
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm64.ActiveCfg = Debug|arm64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm64.Build.0 = Debug|arm64
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|armel.Build.0 = Debug|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|wasm.Build.0 = Debug|Any CPU
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.ActiveCfg = Debug|x64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.Build.0 = Debug|x64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x86.ActiveCfg = Debug|x86
@@ -98,6 +122,10 @@ Global
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm.Build.0 = Release|arm
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm64.ActiveCfg = Release|arm64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm64.Build.0 = Release|arm64
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|armel.ActiveCfg = Release|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|armel.Build.0 = Release|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|wasm.ActiveCfg = Release|Any CPU
+ {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|wasm.Build.0 = Release|Any CPU
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.ActiveCfg = Release|x64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.Build.0 = Release|x64
{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x86.ActiveCfg = Release|x86
@@ -107,6 +135,10 @@ Global
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm.Build.0 = Debug|arm
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm64.ActiveCfg = Debug|arm64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm64.Build.0 = Debug|arm64
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|armel.Build.0 = Debug|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|wasm.Build.0 = Debug|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.ActiveCfg = Debug|x64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.Build.0 = Debug|x64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x86.ActiveCfg = Debug|x86
@@ -116,6 +148,10 @@ Global
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm.Build.0 = Release|arm
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm64.ActiveCfg = Release|arm64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm64.Build.0 = Release|arm64
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|armel.ActiveCfg = Release|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|armel.Build.0 = Release|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|wasm.ActiveCfg = Release|Any CPU
+ {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|wasm.Build.0 = Release|Any CPU
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.ActiveCfg = Release|x64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.Build.0 = Release|x64
{DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x86.ActiveCfg = Release|x86
@@ -125,6 +161,10 @@ Global
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm.Build.0 = Debug|arm
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm64.ActiveCfg = Debug|arm64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm64.Build.0 = Debug|arm64
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|armel.Build.0 = Debug|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|wasm.Build.0 = Debug|Any CPU
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.ActiveCfg = Debug|x64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.Build.0 = Debug|x64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x86.ActiveCfg = Debug|x86
@@ -134,6 +174,10 @@ Global
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm.Build.0 = Release|arm
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm64.ActiveCfg = Release|arm64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm64.Build.0 = Release|arm64
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|armel.ActiveCfg = Release|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|armel.Build.0 = Release|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|wasm.ActiveCfg = Release|Any CPU
+ {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|wasm.Build.0 = Release|Any CPU
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.ActiveCfg = Release|x64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.Build.0 = Release|x64
{13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x86.ActiveCfg = Release|x86
@@ -143,6 +187,10 @@ Global
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm.Build.0 = Debug|arm
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm64.ActiveCfg = Debug|arm64
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm64.Build.0 = Debug|arm64
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|armel.ActiveCfg = Debug|armel
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|armel.Build.0 = Debug|armel
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|wasm.ActiveCfg = Debug|wasm
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|wasm.Build.0 = Debug|wasm
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x64.ActiveCfg = Debug|x64
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x86.ActiveCfg = Debug|x86
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x86.Build.0 = Debug|x86
@@ -151,6 +199,10 @@ Global
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm.Build.0 = Release|arm
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm64.ActiveCfg = Release|arm64
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm64.Build.0 = Release|arm64
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|armel.ActiveCfg = Release|armel
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|armel.Build.0 = Release|armel
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|wasm.ActiveCfg = Release|wasm
+ {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|wasm.Build.0 = Release|wasm
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x64.ActiveCfg = Release|x64
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x64.Build.0 = Release|x64
{BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x86.ActiveCfg = Release|x86
@@ -161,6 +213,10 @@ Global
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm.Build.0 = Debug|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.ActiveCfg = Debug|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.Build.0 = Debug|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|armel.Build.0 = Debug|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|wasm.Build.0 = Debug|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x64.ActiveCfg = Debug|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x64.Build.0 = Debug|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -171,6 +227,10 @@ Global
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm.Build.0 = Release|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.ActiveCfg = Release|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.Build.0 = Release|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|armel.ActiveCfg = Release|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|armel.Build.0 = Release|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|wasm.ActiveCfg = Release|Any CPU
+ {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|wasm.Build.0 = Release|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x64.ActiveCfg = Release|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x64.Build.0 = Release|Any CPU
{D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x86.ActiveCfg = Release|Any CPU
@@ -181,6 +241,10 @@ Global
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm.Build.0 = Debug|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.ActiveCfg = Debug|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.Build.0 = Debug|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|armel.Build.0 = Debug|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|wasm.Build.0 = Debug|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x64.ActiveCfg = Debug|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x64.Build.0 = Debug|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -191,6 +255,10 @@ Global
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm.Build.0 = Release|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.ActiveCfg = Release|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.Build.0 = Release|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|armel.ActiveCfg = Release|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|armel.Build.0 = Release|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|wasm.ActiveCfg = Release|Any CPU
+ {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|wasm.Build.0 = Release|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x64.ActiveCfg = Release|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x64.Build.0 = Release|Any CPU
{A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x86.ActiveCfg = Release|Any CPU
@@ -201,6 +269,10 @@ Global
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm.Build.0 = Debug|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.ActiveCfg = Debug|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.Build.0 = Debug|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|armel.Build.0 = Debug|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|wasm.Build.0 = Debug|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.ActiveCfg = Debug|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.Build.0 = Debug|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -211,6 +283,10 @@ Global
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm.Build.0 = Release|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.ActiveCfg = Release|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.Build.0 = Release|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|armel.ActiveCfg = Release|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|armel.Build.0 = Release|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|wasm.ActiveCfg = Release|Any CPU
+ {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|wasm.Build.0 = Release|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.ActiveCfg = Release|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.Build.0 = Release|Any CPU
{971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x86.ActiveCfg = Release|Any CPU
@@ -221,6 +297,10 @@ Global
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm.Build.0 = Debug|arm
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm64.ActiveCfg = Debug|arm64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm64.Build.0 = Debug|arm64
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|armel.Build.0 = Debug|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|wasm.Build.0 = Debug|Any CPU
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x64.ActiveCfg = Debug|x64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x64.Build.0 = Debug|x64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x86.ActiveCfg = Debug|x86
@@ -231,6 +311,10 @@ Global
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm.Build.0 = Release|arm
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm64.ActiveCfg = Release|arm64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm64.Build.0 = Release|arm64
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|armel.ActiveCfg = Release|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|armel.Build.0 = Release|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|wasm.ActiveCfg = Release|Any CPU
+ {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|wasm.Build.0 = Release|Any CPU
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x64.ActiveCfg = Release|x64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x64.Build.0 = Release|x64
{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x86.ActiveCfg = Release|x86
@@ -241,6 +325,10 @@ Global
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm.Build.0 = Debug|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm64.ActiveCfg = Debug|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm64.Build.0 = Debug|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|armel.Build.0 = Debug|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|wasm.Build.0 = Debug|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x64.ActiveCfg = Debug|x64
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x64.Build.0 = Debug|x64
{CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -251,6 +339,10 @@ Global
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm.Build.0 = Release|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm64.ActiveCfg = Release|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm64.Build.0 = Release|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|armel.ActiveCfg = Release|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|armel.Build.0 = Release|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|wasm.ActiveCfg = Release|Any CPU
+ {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|wasm.Build.0 = Release|Any CPU
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x64.ActiveCfg = Release|x64
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x64.Build.0 = Release|x64
{CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x86.ActiveCfg = Release|Any CPU
@@ -261,6 +353,10 @@ Global
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm.Build.0 = Debug|arm
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm64.ActiveCfg = Debug|arm64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm64.Build.0 = Debug|arm64
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|armel.ActiveCfg = Debug|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|armel.Build.0 = Debug|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|wasm.ActiveCfg = Debug|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|wasm.Build.0 = Debug|Any CPU
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x64.ActiveCfg = Debug|x64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x64.Build.0 = Debug|x64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x86.ActiveCfg = Debug|x86
@@ -271,6 +367,10 @@ Global
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm.Build.0 = Release|arm
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm64.ActiveCfg = Release|arm64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm64.Build.0 = Release|arm64
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|armel.ActiveCfg = Release|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|armel.Build.0 = Release|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|wasm.ActiveCfg = Release|Any CPU
+ {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|wasm.Build.0 = Release|Any CPU
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x64.ActiveCfg = Release|x64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x64.Build.0 = Release|x64
{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x86.ActiveCfg = Release|x86
diff --git a/src/ILCompiler/ObjectWriter/ObjectWriter.depproj b/src/ILCompiler/ObjectWriter/ObjectWriter.depproj
index 95fed2832..d1f46b4ae 100644
--- a/src/ILCompiler/ObjectWriter/ObjectWriter.depproj
+++ b/src/ILCompiler/ObjectWriter/ObjectWriter.depproj
@@ -8,7 +8,9 @@
<NuGetTargetMoniker>.NETCoreApp,Version=v2.0</NuGetTargetMoniker>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">ubuntu.14.04-x64</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="'$(NuPkgRid)'=='win-x64'">win7-x64</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="'$(NuPkgRid)'=='osx-x64'">osx.10.10-x64</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="'$(NuPkgRid)'=='linux-x64'">ubuntu.14.04-x64</RuntimeIdentifiers>
<RidSpecificAssets>true</RidSpecificAssets>
</PropertyGroup>
diff --git a/src/ILCompiler/RyuJIT/RyuJIT.depproj b/src/ILCompiler/RyuJIT/RyuJIT.depproj
index 5e543ee5b..1eb94f741 100644
--- a/src/ILCompiler/RyuJIT/RyuJIT.depproj
+++ b/src/ILCompiler/RyuJIT/RyuJIT.depproj
@@ -8,9 +8,6 @@
<NuGetTargetMoniker>.NETCoreApp,Version=v2.0</NuGetTargetMoniker>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('win7'))">win-x64</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('osx.'))">osx-x64</RuntimeIdentifiers>
- <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">linux-x64</RuntimeIdentifiers>
<RidSpecificAssets>true</RidSpecificAssets>
</PropertyGroup>
diff --git a/src/ILCompiler/repro/repro.csproj b/src/ILCompiler/repro/repro.csproj
index 15ed06b83..5e3e75312 100644
--- a/src/ILCompiler/repro/repro.csproj
+++ b/src/ILCompiler/repro/repro.csproj
@@ -7,6 +7,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<SkipSigning>true</SkipSigning>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <CopyNuGetImplementations>false</CopyNuGetImplementations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
diff --git a/src/ILCompiler/reproNative/reproNative.vcxproj b/src/ILCompiler/reproNative/reproNative.vcxproj
index 654438da2..9d9f6eed2 100644
--- a/src/ILCompiler/reproNative/reproNative.vcxproj
+++ b/src/ILCompiler/reproNative/reproNative.vcxproj
@@ -13,7 +13,7 @@
<ProjectGuid>{ECB5D162-A31B-45FF-87C7-2E92BD445F5A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>reproNative</RootNamespace>
- <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@@ -104,4 +104,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/src/ILVerification/README.md b/src/ILVerification/README.md
new file mode 100644
index 000000000..6056ba59a
--- /dev/null
+++ b/src/ILVerification/README.md
@@ -0,0 +1,3 @@
+# ILVerification
+
+The ILVerification library is part of the ILVerify project. See details under [src/ILVerify/](https://github.com/dotnet/corert/tree/master/src/ILVerify). \ No newline at end of file
diff --git a/src/ILVerification/StrongNameKeys/ILVerify.snk b/src/ILVerification/StrongNameKeys/ILVerify.snk
new file mode 100644
index 000000000..e5534f1a9
--- /dev/null
+++ b/src/ILVerification/StrongNameKeys/ILVerify.snk
Binary files differ
diff --git a/src/ILVerify/src/AccessVerificationHelpers.cs b/src/ILVerification/src/AccessVerificationHelpers.cs
index 896643273..b27affb07 100644
--- a/src/ILVerify/src/AccessVerificationHelpers.cs
+++ b/src/ILVerification/src/AccessVerificationHelpers.cs
@@ -149,7 +149,7 @@ namespace ILVerify
break;
case MethodAttributes.Family:
case MethodAttributes.FamANDAssem:
- // Assembly acces was already checked earlier, so only need to check family access
+ // Assembly access was already checked earlier, so only need to check family access
if (CanAccessFamily(currentType, targetTypeDef, instance))
return true;
break;
diff --git a/src/ILVerification/src/AssemblyInfo.cs b/src/ILVerification/src/AssemblyInfo.cs
new file mode 100644
index 000000000..cd0ba8baa
--- /dev/null
+++ b/src/ILVerification/src/AssemblyInfo.cs
@@ -0,0 +1,4 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("ILVerification.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010015c01ae1f50e8cc09ba9eac9147cf8fd9fce2cfe9f8dce4f7301c4132ca9fb50ce8cbf1df4dc18dd4d210e4345c744ecb3365ed327efdbc52603faa5e21daa11234c8c4a73e51f03bf192544581ebe107adee3a34928e39d04e524a9ce729d5090bfd7dad9d10c722c0def9ccc08ff0a03790e48bcd1f9b6c476063e1966a1c4")]
+[assembly: InternalsVisibleTo("ILVerify, PublicKey=0024000004800000940000000602000000240000525341310004000001000100314e026e409db99b50d61628136c49095e67f782a3032832cfe1e61ba2af8264d2cf7d9228bdf611c1027f61b0ca4c87ee1c248cd58241a695520ba78e76d1c672c2b597cfa0ab4526dcae2b5b6f36936c126e59ada3500d656f3424826d0dab452ea407039d2846cf0e4820905eee537fe904a86097b5b2f3aaae000fc08fc3")]
diff --git a/src/ILVerify/src/ILImporter.StackValue.cs b/src/ILVerification/src/ILImporter.StackValue.cs
index ad2497b1c..ad2497b1c 100644
--- a/src/ILVerify/src/ILImporter.StackValue.cs
+++ b/src/ILVerification/src/ILImporter.StackValue.cs
diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerification/src/ILImporter.Verify.cs
index 5dde3d6f3..cb168b88a 100644
--- a/src/ILVerify/src/ILImporter.Verify.cs
+++ b/src/ILVerification/src/ILImporter.Verify.cs
@@ -26,13 +26,11 @@ namespace Internal.IL
}
}
- struct VerificationErrorArgs
+ class VerifierException : Exception
{
- public VerifierError Code;
- public int Offset;
- public int Token;
- public string Found;
- public string Expected;
+ internal VerifierException(string message) : base(message)
+ {
+ }
}
partial class ILImporter
diff --git a/src/ILVerification/src/ILVerification.csproj b/src/ILVerification/src/ILVerification.csproj
new file mode 100644
index 000000000..efa6e5349
--- /dev/null
+++ b/src/ILVerification/src/ILVerification.csproj
@@ -0,0 +1,302 @@
+<Project>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <CLSCompliant>false</CLSCompliant>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ <TargetFramework>netstandard1.3</TargetFramework>
+ <AssemblyKey></AssemblyKey>
+ <AssemblyOriginatorKeyFile>..\StrongNameKeys\ILVerify.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="AssemblyInfo.cs" />
+ <Compile Include="ILImporter.Verify.cs" />
+ <Compile Include="ILImporter.StackValue.cs" />
+ <Compile Include="SimpleArrayOfTRuntimeInterfacesAlgorithm.cs" />
+ <Compile Include="ILVerifyTypeSystemContext.cs" />
+ <Compile Include="Verifier.cs" />
+ <Compile Include="VerifierError.cs" />
+ <Compile Include="TypeSystemHelpers.cs" />
+ <Compile Include="InstantiatedGenericParameter.cs" />
+ <Compile Include="AccessVerificationHelpers.cs" />
+ <Compile Include="VerificationResult.cs" />
+ <Compile Include="IResolver.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\..\Common\src\TypeSystem\CodeGen\MethodDesc.CodeGen.cs">
+ <Link>TypeSystem\CodeGen\MethodDesc.CodeGen.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\AlignmentHelper.cs">
+ <Link>Utilities\AlignmentHelper.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\CastingHelper.cs">
+ <Link>TypeSystem\Common\CastingHelper.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\FunctionPointerType.cs">
+ <Link>TypeSystem\Common\FunctionPointerType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\IAssemblyDesc.cs">
+ <Link>TypeSystem\Common\IAssemblyDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Instantiation.cs">
+ <Link>TypeSystem\Common\Instantiation.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ModuleDesc.cs">
+ <Link>TypeSystem\Common\ModuleDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemEntity.cs">
+ <Link>TypeSystem\Common\TypeSystemEntity.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemException.cs">
+ <Link>TypeSystem\Common\TypeSystemException.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\CustomAttributeTypeNameParser.cs">
+ <Link>Utilities\CustomAttributeTypeNameParser.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\LockFreeReaderHashtable.cs">
+ <Link>Utilities\LockFreeReaderHashtable.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ArrayType.cs">
+ <Link>TypeSystem\Common\ArrayType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ArrayOfTRuntimeInterfacesAlgorithm.cs">
+ <Link>TypeSystem\Common\ArrayOfTRuntimeInterfacesAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\BaseTypeRuntimeInterfacesAlgorithm.cs">
+ <Link>TypeSystem\Common\BaseTypeRuntimeInterfacesAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ByRefType.cs">
+ <Link>TypeSystem\Common\ByRefType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\GenericParameterDesc.cs">
+ <Link>TypeSystem\Common\GenericParameterDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ExceptionStringID.cs">
+ <Link>TypeSystem\Common\ExceptionStringID.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\FieldForInstantiatedType.cs">
+ <Link>TypeSystem\Common\FieldForInstantiatedType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\FieldDesc.cs">
+ <Link>TypeSystem\Common\FieldDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\FieldDesc.FieldLayout.cs">
+ <Link>TypeSystem\Common\FieldDesc.FieldLayout.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\FieldLayoutAlgorithm.cs">
+ <Link>TypeSystem\Common\FieldLayoutAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedMethod.cs">
+ <Link>TypeSystem\Common\InstantiatedMethod.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.cs">
+ <Link>TypeSystem\Common\InstantiatedType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.Interfaces.cs">
+ <Link>TypeSystem\Common\InstantiatedType.Interfaces.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.MethodImpls.cs">
+ <Link>TypeSystem\Common\InstantiatedType.MethodImpls.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\LayoutInt.cs">
+ <Link>TypeSystem\Common\LayoutInt.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.cs">
+ <Link>TypeSystem\Common\MetadataType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.Interfaces.cs">
+ <Link>TypeSystem\Common\MetadataType.Interfaces.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.MethodImpls.cs">
+ <Link>TypeSystem\Common\MetadataType.MethodImpls.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataFieldLayoutAlgorithm.cs">
+ <Link>TypeSystem\Common\MetadataFieldLayoutAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataRuntimeInterfacesAlgorithm.cs">
+ <Link>TypeSystem\Common\MetadataRuntimeInterfacesAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataTypeSystemContext.cs">
+ <Link>TypeSystem\Common\MetadataTypeSystemContext.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MethodForInstantiatedType.cs">
+ <Link>TypeSystem\Common\MethodForInstantiatedType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ParameterizedType.cs">
+ <Link>TypeSystem\Common\ParameterizedType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\PointerType.cs">
+ <Link>TypeSystem\Common\PointerType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\PropertySignature.cs">
+ <Link>TypeSystem\Common\PropertySignature.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\SignatureVariable.cs">
+ <Link>TypeSystem\Common\SignatureVariable.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TargetDetails.cs">
+ <Link>TypeSystem\Common\TargetDetails.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ThreadSafeFlags.cs">
+ <Link>TypeSystem\Common\ThreadSafeFlags.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeFlags.cs">
+ <Link>TypeSystem\Common\TypeFlags.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeHashingAlgorithms.cs">
+ <Link>TypeSystem\Common\TypeHashingAlgorithms.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemContext.cs">
+ <Link>TypeSystem\Common\TypeSystemContext.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemHelpers.cs">
+ <Link>TypeSystem\Common\TypeSystemHelpers.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\TypeNameFormatter.cs">
+ <Link>Utilities\TypeNameFormatter.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\WellKnownType.cs">
+ <Link>TypeSystem\Common\WellKnownType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\VirtualMethodAlgorithm.cs">
+ <Link>TypeSystem\Common\VirtualMethodAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MethodDesc.cs">
+ <Link>TypeSystem\Common\MethodDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataVirtualMethodAlgorithm.cs">
+ <Link>TypeSystem\Common\StandardVirtualMethodAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeDesc.cs">
+ <Link>TypeSystem\Common\TypeDesc.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeDesc.Interfaces.cs">
+ <Link>TypeSystem\Common\TypeDesc.Interfaces.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\DefType.cs">
+ <Link>TypeSystem\Common\DefType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\DefType.FieldLayout.cs">
+ <Link>TypeSystem\Common\DefType.FieldLayout.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\RuntimeInterfacesAlgorithm.cs">
+ <Link>TypeSystem\Common\RuntimeInterfacesAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ThrowHelper.Common.cs">
+ <Link>TypeSystem\Common\ThrowHelper.Common.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\ThrowHelper.cs">
+ <Link>TypeSystem\Common\ThrowHelper.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs">
+ <Link>TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.Metadata.cs">
+ <Link>TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.Metadata.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\CustomAttributeTypeProvider.cs">
+ <Link>Ecma\CustomAttributeTypeProvider.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaAssembly.cs">
+ <Link>Ecma\EcmaAssembly.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaField.cs">
+ <Link>Ecma\EcmaField.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaGenericParameter.cs">
+ <Link>Ecma\EcmaGenericParameter.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaMethod.cs">
+ <Link>Ecma\EcmaMethod.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaModule.cs">
+ <Link>Ecma\EcmaModule.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaSignatureParser.cs">
+ <Link>Ecma\EcmaSignatureParser.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.cs">
+ <Link>Ecma\EcmaType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.MethodImpls.cs">
+ <Link>Ecma\EcmaType.MethodImpls.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.Interfaces.cs">
+ <Link>Ecma\EcmaType.Interfaces.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\MetadataExtensions.cs">
+ <Link>Ecma\MetadataExtensions.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\IMetadataStringDecoderProvider.cs">
+ <Link>Ecma\IMetadataStringDecoderProvider.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\CachingMetadataStringDecoder.cs">
+ <Link>Ecma\CachingMetadataStringDecoder.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Ecma\PrimitiveTypeProvider.cs">
+ <Link>Ecma\PrimitiveTypeProvider.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\EcmaMethodIL.cs">
+ <Link>IL\EcmaMethodIL.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\MethodIL.cs">
+ <Link>IL\MethodIL.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\MethodILDebugView.cs">
+ <Link>IL\MethodILDebugView.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILDisassembler.cs">
+ <Link>IL\ILDisassembler.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\InstantiatedMethodIL.cs">
+ <Link>IL\InstantiatedMethodIL.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcode.cs">
+ <Link>IL\ILOpcode.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILImporter.cs">
+ <Link>IL\ILImporter.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Interop\InstantiatedType.Interop.cs">
+ <Link>Interop\InstantiatedType.Interop.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Interop\MetadataType.Interop.cs">
+ <Link>Interop\MetadataType.Interop.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Interop\MethodDesc.Interop.cs">
+ <Link>Interop\MethodDesc.Interop.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Interop\MarshalAsDescriptor.cs">
+ <Link>TypeSystem\Interop\MarshalAsDescriptor.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\System\Collections\Generic\ArrayBuilder.cs">
+ <Link>Utilities\ArrayBuilder.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\LocalVariableDefinition.cs">
+ <Link>TypeSystem\Common\LocalVariableDefinition.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\System\FormattingHelpers.cs">
+ <Link>Common\System\FormattingHelpers.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemConstraintsHelpers.cs">
+ <Link>TypeSystem\Common\TypeSystemConstraintsHelpers.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\..\Common\src\System\NotImplemented.cs">
+ <Link>System\NotImplemented.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\..\ILVerification\StrongNameKeys\ILVerify.snk" Link="ILVerify.snk" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="System.IO.MemoryMappedFiles" Version="4.3.0" />
+ <PackageReference Include="System.Reflection.Metadata" Version="1.4.1" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/ILVerification/src/ILVerifyTypeSystemContext.cs b/src/ILVerification/src/ILVerifyTypeSystemContext.cs
new file mode 100644
index 000000000..528dc6a90
--- /dev/null
+++ b/src/ILVerification/src/ILVerifyTypeSystemContext.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Internal.IL;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILVerify
+{
+ class ILVerifyTypeSystemContext : MetadataTypeSystemContext
+ {
+ internal readonly IResolver _resolver;
+
+ private RuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
+ private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm();
+
+ private readonly Dictionary<PEReader, EcmaModule> _modulesCache = new Dictionary<PEReader, EcmaModule>();
+
+ public ILVerifyTypeSystemContext(IResolver resolver)
+ {
+ _resolver = resolver;
+ }
+
+ public override ModuleDesc ResolveAssembly(AssemblyName name, bool throwIfNotFound = true)
+ {
+ PEReader peReader = _resolver.Resolve(name);
+ if (peReader == null && throwIfNotFound)
+ {
+ throw new VerifierException("Assembly or module not found: " + name.Name);
+ }
+
+ var module = GetModule(peReader);
+ VerifyModuleName(name, module);
+ return module;
+ }
+
+ private static void VerifyModuleName(AssemblyName name, EcmaModule module)
+ {
+ MetadataReader metadataReader = module.MetadataReader;
+ StringHandle nameHandle = metadataReader.IsAssembly
+ ? metadataReader.GetAssemblyDefinition().Name
+ : metadataReader.GetModuleDefinition().Name;
+
+ string actualSimpleName = metadataReader.GetString(nameHandle);
+ if (!actualSimpleName.Equals(name.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new VerifierException($"Actual PE name '{actualSimpleName}' does not match provided name '{name}'");
+ }
+ }
+
+ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type)
+ {
+ if (_arrayOfTRuntimeInterfacesAlgorithm == null)
+ {
+ _arrayOfTRuntimeInterfacesAlgorithm = new SimpleArrayOfTRuntimeInterfacesAlgorithm(SystemModule);
+ }
+ return _arrayOfTRuntimeInterfacesAlgorithm;
+ }
+
+ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type)
+ {
+ return _metadataRuntimeInterfacesAlgorithm;
+ }
+
+ internal EcmaModule GetModule(PEReader peReader)
+ {
+ if (peReader == null)
+ {
+ return null;
+ }
+
+ if (_modulesCache.TryGetValue(peReader, out EcmaModule existingModule))
+ {
+ return existingModule;
+ }
+
+ EcmaModule module = EcmaModule.Create(this, peReader);
+ _modulesCache.Add(peReader, module);
+ return module;
+ }
+ }
+}
diff --git a/src/ILVerification/src/IResolver.cs b/src/ILVerification/src/IResolver.cs
new file mode 100644
index 000000000..f3706cf3b
--- /dev/null
+++ b/src/ILVerification/src/IResolver.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.PortableExecutable;
+
+namespace ILVerify
+{
+ public interface IResolver
+ {
+ /// <summary>
+ /// This method should return the same instance when queried multiple times.
+ /// </summary>
+ PEReader Resolve(AssemblyName name);
+ }
+
+ /// <summary>
+ /// Provides caching logic for implementations of IResolver
+ /// </summary>
+ public abstract class ResolverBase : IResolver
+ {
+ private readonly Dictionary<string, PEReader> _resolverCache = new Dictionary<string, PEReader>();
+
+ public PEReader Resolve(AssemblyName name)
+ {
+ // Note: we use simple names instead of full names to resolve, because we can't get a full name from an assembly without reading it
+ string simpleName = name.Name;
+ if (_resolverCache.TryGetValue(simpleName, out PEReader peReader))
+ {
+ return peReader;
+ }
+
+ PEReader result = ResolveCore(name);
+ if (result != null)
+ {
+ _resolverCache.Add(simpleName, result);
+ return result;
+ }
+
+ return null;
+ }
+
+ protected abstract PEReader ResolveCore(AssemblyName name);
+ }
+}
diff --git a/src/ILVerify/src/InstantiatedGenericParameter.cs b/src/ILVerification/src/InstantiatedGenericParameter.cs
index a767de4fe..a767de4fe 100644
--- a/src/ILVerify/src/InstantiatedGenericParameter.cs
+++ b/src/ILVerification/src/InstantiatedGenericParameter.cs
diff --git a/src/ILVerify/src/Resources/Strings.resx b/src/ILVerification/src/Resources/Strings.resx
index 63ab4cb01..63ab4cb01 100644
--- a/src/ILVerify/src/Resources/Strings.resx
+++ b/src/ILVerification/src/Resources/Strings.resx
diff --git a/src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs b/src/ILVerification/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs
index 5e62168f0..5e62168f0 100644
--- a/src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs
+++ b/src/ILVerification/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs
diff --git a/src/ILVerify/src/TypeSystemHelpers.cs b/src/ILVerification/src/TypeSystemHelpers.cs
index 52c5fd987..52c5fd987 100644
--- a/src/ILVerify/src/TypeSystemHelpers.cs
+++ b/src/ILVerification/src/TypeSystemHelpers.cs
diff --git a/src/ILVerification/src/VerificationResult.cs b/src/ILVerification/src/VerificationResult.cs
new file mode 100644
index 000000000..e64208832
--- /dev/null
+++ b/src/ILVerification/src/VerificationResult.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection.Metadata;
+
+namespace ILVerify
+{
+ public class VerificationResult
+ {
+ public MethodDefinitionHandle Method { get; internal set; }
+ public VerificationErrorArgs Error { get; internal set; }
+ public string Message { get; internal set; }
+ }
+
+ public struct VerificationErrorArgs
+ {
+ public VerifierError Code { get; internal set; }
+ public int Offset { get; internal set; }
+ public int Token { get; internal set; }
+ public string Found { get; internal set; }
+ public string Expected { get; internal set; }
+ }
+}
diff --git a/src/ILVerification/src/Verifier.cs b/src/ILVerification/src/Verifier.cs
new file mode 100644
index 000000000..9341ef395
--- /dev/null
+++ b/src/ILVerification/src/Verifier.cs
@@ -0,0 +1,234 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Resources;
+using Internal.IL;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILVerify
+{
+ public class Verifier
+ {
+ private Lazy<ResourceManager> _stringResourceManager =
+ new Lazy<ResourceManager>(() => new ResourceManager("FxResources.ILVerification.SR", typeof(Verifier).GetTypeInfo().Assembly));
+
+ private ILVerifyTypeSystemContext _typeSystemContext;
+
+ public Verifier(IResolver resolver)
+ {
+ _typeSystemContext = new ILVerifyTypeSystemContext(resolver);
+ }
+
+ internal Verifier(ILVerifyTypeSystemContext context)
+ {
+ _typeSystemContext = context;
+ }
+
+ public void SetSystemModuleName(AssemblyName name)
+ {
+ _typeSystemContext.SetSystemModule(_typeSystemContext.GetModule(_typeSystemContext._resolver.Resolve(name)));
+ }
+
+ internal EcmaModule GetModule(PEReader peReader)
+ {
+ return _typeSystemContext.GetModule(peReader);
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ results = VerifyMethods(module, module.MetadataReader.MethodDefinitions);
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader, TypeDefinitionHandle typeHandle)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (typeHandle.IsNil)
+ {
+ throw new ArgumentNullException(nameof(typeHandle));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ TypeDefinition typeDef = peReader.GetMetadataReader().GetTypeDefinition(typeHandle);
+ results = VerifyMethods(module, typeDef.GetMethods());
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader, MethodDefinitionHandle methodHandle)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (methodHandle.IsNil)
+ {
+ throw new ArgumentNullException(nameof(methodHandle));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ results = VerifyMethods(module, new[] { methodHandle });
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ private IEnumerable<VerificationResult> VerifyMethods(EcmaModule module, IEnumerable<MethodDefinitionHandle> methodHandles)
+ {
+ foreach (var methodHandle in methodHandles)
+ {
+ var method = (EcmaMethod)module.GetMethod(methodHandle);
+ var methodIL = EcmaMethodIL.Create(method);
+
+ if (methodIL != null)
+ {
+ var results = VerifyMethod(module, methodIL, methodHandle);
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+ }
+ }
+
+ private IEnumerable<VerificationResult> VerifyMethod(EcmaModule module, MethodIL methodIL, MethodDefinitionHandle methodHandle)
+ {
+ var builder = new ArrayBuilder<VerificationResult>();
+ MethodDesc method = methodIL.OwningMethod;
+
+ try
+ {
+ var importer = new ILImporter(method, methodIL);
+
+ importer.ReportVerificationError = (args) =>
+ {
+ var codeResource = _stringResourceManager.Value.GetString(args.Code.ToString(), CultureInfo.InvariantCulture);
+
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Error = args,
+ Message = string.IsNullOrEmpty(codeResource) ? args.Code.ToString() : codeResource
+ });
+ };
+
+ importer.Verify();
+ }
+ catch (VerificationException)
+ {
+ // a result was reported already (before aborting)
+ }
+ catch (BadImageFormatException)
+ {
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Message = "Unable to resolve token"
+ });
+ }
+ catch (NotImplementedException e)
+ {
+ reportException(e);
+ }
+ catch (InvalidProgramException e)
+ {
+ reportException(e);
+ }
+ catch (PlatformNotSupportedException e)
+ {
+ reportException(e);
+ }
+ catch (VerifierException e)
+ {
+ reportException(e);
+ }
+ catch (TypeSystemException e)
+ {
+ reportException(e);
+ }
+
+ return builder.ToArray();
+
+ void reportException(Exception e)
+ {
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Message = e.Message
+ });
+ }
+ }
+
+ private void ThrowMissingSystemModule()
+ {
+ throw new VerifierException("No system module specified");
+ }
+ }
+}
diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerification/src/VerifierError.cs
index 8b2db4831..508fe5154 100644
--- a/src/ILVerify/src/VerifierError.cs
+++ b/src/ILVerification/src/VerifierError.cs
@@ -2,16 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace ILVerify
{
- enum VerifierError
+ public enum VerifierError
{
+ None = 0,
//E_HRESULT "[HRESULT 0x%08X]"
//E_OFFSET "[offset 0x%08X]"
//E_OPCODE "[opcode %s]"
diff --git a/src/ILVerify/tests/ILMethodTester.cs b/src/ILVerification/tests/ILMethodTester.cs
index 671bd3f91..eb92c3dcb 100644
--- a/src/ILVerify/tests/ILMethodTester.cs
+++ b/src/ILVerification/tests/ILMethodTester.cs
@@ -2,15 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
-using Internal.IL;
+using ILVerify;
using Internal.TypeSystem.Ecma;
using Xunit;
-namespace ILVerify.Tests
+namespace ILVerification.Tests
{
public class ILMethodTester
{
@@ -19,16 +19,8 @@ namespace ILVerify.Tests
[Trait("", "Valid IL Tests")]
void TestMethodsWithValidIL(ValidILTestCase validIL)
{
- ILImporter importer = ConstructILImporter(validIL);
-
- var verifierErrors = new List<VerifierError>();
- importer.ReportVerificationError = new Action<VerificationErrorArgs>((err) =>
- {
- verifierErrors.Add(err.Code);
- });
-
- importer.Verify();
- Assert.Equal(0, verifierErrors.Count);
+ var results = Verify(validIL);
+ Assert.Equal(0, results.Count());
}
[Theory(DisplayName = "")]
@@ -36,17 +28,11 @@ namespace ILVerify.Tests
[Trait("", "Invalid IL Tests")]
void TestMethodsWithInvalidIL(InvalidILTestCase invalidIL)
{
- ILImporter importer = ConstructILImporter(invalidIL);
-
- var verifierErrors = new List<VerifierError>();
- importer.ReportVerificationError = new Action<VerificationErrorArgs>((err) =>
- {
- verifierErrors.Add(err.Code);
- });
+ IEnumerable<VerificationResult> results = null;
try
{
- importer.Verify();
+ results = Verify(invalidIL);
}
catch
{
@@ -57,23 +43,24 @@ namespace ILVerify.Tests
}
finally
{
- Assert.Equal(invalidIL.ExpectedVerifierErrors.Count, verifierErrors.Count);
+ Assert.NotNull(results);
+ Assert.Equal(invalidIL.ExpectedVerifierErrors.Count, results.Count());
foreach (var item in invalidIL.ExpectedVerifierErrors)
{
- var actual = verifierErrors.Select(e => e.ToString());
- Assert.True(verifierErrors.Contains(item), $"Actual errors where: {string.Join(',', actual)}");
+ var actual = results.Select(e => e.ToString());
+ Assert.True(results.Where(r => r.Error.Code == item).Count() > 0, $"Actual errors where: {string.Join(",", actual)}");
}
}
}
- private ILImporter ConstructILImporter(TestCase testCase)
+ private static IEnumerable<VerificationResult> Verify(TestCase testCase)
{
- var module = TestDataLoader.GetModuleForTestAssembly(testCase.ModuleName);
- var method = (EcmaMethod)module.GetMethod(MetadataTokens.EntityHandle(testCase.MetadataToken));
- var methodIL = EcmaMethodIL.Create(method);
-
- return new ILImporter(method, methodIL);
+ EcmaModule module = TestDataLoader.GetModuleForTestAssembly(testCase.ModuleName);
+ var methodHandle = (MethodDefinitionHandle) MetadataTokens.EntityHandle(testCase.MetadataToken);
+ var method = (EcmaMethod)module.GetMethod(methodHandle);
+ var verifier = new Verifier((ILVerifyTypeSystemContext)method.Context);
+ return verifier.Verify(module.PEReader, methodHandle);
}
}
}
diff --git a/src/ILVerify/tests/ILTests/AccessTests.il b/src/ILVerification/tests/ILTests/AccessTests.il
index 9de153c3c..9de153c3c 100644
--- a/src/ILVerify/tests/ILTests/AccessTests.il
+++ b/src/ILVerification/tests/ILTests/AccessTests.il
diff --git a/src/ILVerify/tests/ILTests/AccessTestsExtern.il b/src/ILVerification/tests/ILTests/AccessTestsExtern.il
index 39939e1bf..39939e1bf 100644
--- a/src/ILVerify/tests/ILTests/AccessTestsExtern.il
+++ b/src/ILVerification/tests/ILTests/AccessTestsExtern.il
diff --git a/src/ILVerify/tests/ILTests/AccessTestsFriend.il b/src/ILVerification/tests/ILTests/AccessTestsFriend.il
index 78db6d8ad..78db6d8ad 100644
--- a/src/ILVerify/tests/ILTests/AccessTestsFriend.il
+++ b/src/ILVerification/tests/ILTests/AccessTestsFriend.il
diff --git a/src/ILVerify/tests/ILTests/ArrayTests.il b/src/ILVerification/tests/ILTests/ArrayTests.il
index 7f4cb903a..7f4cb903a 100644
--- a/src/ILVerify/tests/ILTests/ArrayTests.il
+++ b/src/ILVerification/tests/ILTests/ArrayTests.il
diff --git a/src/ILVerify/tests/ILTests/BasicArithmeticTests.il b/src/ILVerification/tests/ILTests/BasicArithmeticTests.il
index 8ce554d86..8ce554d86 100644
--- a/src/ILVerify/tests/ILTests/BasicArithmeticTests.il
+++ b/src/ILVerification/tests/ILTests/BasicArithmeticTests.il
diff --git a/src/ILVerify/tests/ILTests/BranchingTests.il b/src/ILVerification/tests/ILTests/BranchingTests.il
index dd5258c22..dd5258c22 100644
--- a/src/ILVerify/tests/ILTests/BranchingTests.il
+++ b/src/ILVerification/tests/ILTests/BranchingTests.il
diff --git a/src/ILVerify/tests/ILTests/CallTests.il b/src/ILVerification/tests/ILTests/CallTests.il
index a0637a25c..a0637a25c 100644
--- a/src/ILVerify/tests/ILTests/CallTests.il
+++ b/src/ILVerification/tests/ILTests/CallTests.il
diff --git a/src/ILVerify/tests/ILTests/CastingTests.il b/src/ILVerification/tests/ILTests/CastingTests.il
index bf754a53c..bf754a53c 100644
--- a/src/ILVerify/tests/ILTests/CastingTests.il
+++ b/src/ILVerification/tests/ILTests/CastingTests.il
diff --git a/src/ILVerify/tests/ILTests/ComparisonTests.il b/src/ILVerification/tests/ILTests/ComparisonTests.il
index 28797f958..28797f958 100644
--- a/src/ILVerify/tests/ILTests/ComparisonTests.il
+++ b/src/ILVerification/tests/ILTests/ComparisonTests.il
diff --git a/src/ILVerify/tests/ILTests/DelegateTests.il b/src/ILVerification/tests/ILTests/DelegateTests.il
index 12ef7c57a..12ef7c57a 100644
--- a/src/ILVerify/tests/ILTests/DelegateTests.il
+++ b/src/ILVerification/tests/ILTests/DelegateTests.il
diff --git a/src/ILVerify/tests/ILTests/ExceptionRegionTests.il b/src/ILVerification/tests/ILTests/ExceptionRegionTests.il
index 379d25ffc..379d25ffc 100644
--- a/src/ILVerify/tests/ILTests/ExceptionRegionTests.il
+++ b/src/ILVerification/tests/ILTests/ExceptionRegionTests.il
diff --git a/src/ILVerify/tests/ILTests/FieldTests.il b/src/ILVerification/tests/ILTests/FieldTests.il
index 816f6fee7..816f6fee7 100644
--- a/src/ILVerify/tests/ILTests/FieldTests.il
+++ b/src/ILVerification/tests/ILTests/FieldTests.il
diff --git a/src/ILVerify/tests/ILTests/FtnTests.il b/src/ILVerification/tests/ILTests/FtnTests.il
index c3a0aec45..c3a0aec45 100644
--- a/src/ILVerify/tests/ILTests/FtnTests.il
+++ b/src/ILVerification/tests/ILTests/FtnTests.il
diff --git a/src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il b/src/ILVerification/tests/ILTests/LoadStoreIndirectTests.il
index ecf0545c5..ecf0545c5 100644
--- a/src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il
+++ b/src/ILVerification/tests/ILTests/LoadStoreIndirectTests.il
diff --git a/src/ILVerify/tests/ILTests/NewobjTests.il b/src/ILVerification/tests/ILTests/NewobjTests.il
index 38d121c72..38d121c72 100644
--- a/src/ILVerify/tests/ILTests/NewobjTests.il
+++ b/src/ILVerification/tests/ILTests/NewobjTests.il
diff --git a/src/ILVerify/tests/ILTests/PrefixTests.il b/src/ILVerification/tests/ILTests/PrefixTests.il
index f53782d76..f53782d76 100644
--- a/src/ILVerify/tests/ILTests/PrefixTests.il
+++ b/src/ILVerification/tests/ILTests/PrefixTests.il
diff --git a/src/ILVerify/tests/ILTests/ReturnTests.il b/src/ILVerification/tests/ILTests/ReturnTests.il
index 4cc0b5081..4cc0b5081 100644
--- a/src/ILVerify/tests/ILTests/ReturnTests.il
+++ b/src/ILVerification/tests/ILTests/ReturnTests.il
diff --git a/src/ILVerify/tests/ILTests/ShiftTests.il b/src/ILVerification/tests/ILTests/ShiftTests.il
index d0b7f8484..d0b7f8484 100644
--- a/src/ILVerify/tests/ILTests/ShiftTests.il
+++ b/src/ILVerification/tests/ILTests/ShiftTests.il
diff --git a/src/ILVerify/tests/ILTests/SwitchTests.il b/src/ILVerification/tests/ILTests/SwitchTests.il
index 974af29b6..974af29b6 100644
--- a/src/ILVerify/tests/ILTests/SwitchTests.il
+++ b/src/ILVerification/tests/ILTests/SwitchTests.il
diff --git a/src/ILVerify/tests/ILTests/ThisStateTests.il b/src/ILVerification/tests/ILTests/ThisStateTests.il
index 0c55f4451..0c55f4451 100644
--- a/src/ILVerify/tests/ILTests/ThisStateTests.il
+++ b/src/ILVerification/tests/ILTests/ThisStateTests.il
diff --git a/src/ILVerify/tests/ILTests/ValueTypeTests.il b/src/ILVerification/tests/ILTests/ValueTypeTests.il
index f38117203..f38117203 100644
--- a/src/ILVerify/tests/ILTests/ValueTypeTests.il
+++ b/src/ILVerification/tests/ILTests/ValueTypeTests.il
diff --git a/src/ILVerification/tests/ILVerification.Tests.csproj b/src/ILVerification/tests/ILVerification.Tests.csproj
new file mode 100644
index 000000000..4d5f17abf
--- /dev/null
+++ b/src/ILVerification/tests/ILVerification.Tests.csproj
@@ -0,0 +1,39 @@
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <TargetFramework>netstandard1.5</TargetFramework>
+
+ <IsPackable>false</IsPackable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="ILMethodTester.cs" />
+ <Compile Include="TestDataLoader.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170427-09" />
+ <PackageReference Include="xunit" Version="2.2.0" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
+ <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\src\ILVerification.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Folder Include="ILTests\" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Include="..\..\ILVerification\StrongNameKeys\ILVerify.snk" Link="ILVerify.snk" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/ILVerify/tests/TestDataLoader.cs b/src/ILVerification/tests/TestDataLoader.cs
index 15dc8efde..91a1195f8 100644
--- a/src/ILVerify/tests/TestDataLoader.cs
+++ b/src/ILVerification/tests/TestDataLoader.cs
@@ -8,15 +8,15 @@ using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
using System.Text;
-using Internal.IL;
-using Internal.TypeSystem;
+using ILVerify;
using Internal.TypeSystem.Ecma;
using Newtonsoft.Json;
using Xunit;
using Xunit.Abstractions;
-namespace ILVerify.Tests
+namespace ILVerification.Tests
{
/// <summary>
/// Parses the methods in the test assemblies.
@@ -96,7 +96,7 @@ namespace ILVerify.Tests
private static TheoryData<TestCase> GetTestMethodsFromDll(Func<string[], MethodDefinitionHandle, TestCase> methodSelector)
{
- var retVal = new Xunit.TheoryData<TestCase>();
+ var retVal = new TheoryData<TestCase>();
foreach (var testDllName in GetAllTestDlls())
{
@@ -152,33 +152,54 @@ namespace ILVerify.Tests
private static IEnumerable<string> GetAllTestDlls()
{
- foreach (var item in System.IO.Directory.GetFiles(TESTASSEMBLYPATH))
+ foreach (var item in Directory.GetFiles(TESTASSEMBLYPATH))
{
if (item.ToLower().EndsWith(".dll"))
{
- yield return System.IO.Path.GetFileName(item);
+ yield return Path.GetFileName(item);
}
}
}
public static EcmaModule GetModuleForTestAssembly(string assemblyName)
{
- var typeSystemContext = new SimpleTypeSystemContext();
- var coreAssembly = typeof(Object).Assembly;
- var systemRuntime = Assembly.Load("System.Runtime");
+ var simpleNameToPathMap = new Dictionary<string, string>();
- typeSystemContext.InputFilePaths = new Dictionary<string, string>
+ foreach (var fileName in GetAllTestDlls())
{
- { coreAssembly.GetName().Name, coreAssembly.Location },
- { systemRuntime.GetName().Name, systemRuntime.Location }
- };
+ simpleNameToPathMap.Add(Path.GetFileNameWithoutExtension(fileName), TESTASSEMBLYPATH + fileName);
+ }
- typeSystemContext.ReferenceFilePaths = new Dictionary<string, string>();
- foreach (var fileName in GetAllTestDlls())
- typeSystemContext.ReferenceFilePaths.Add(Path.GetFileNameWithoutExtension(fileName), TESTASSEMBLYPATH + fileName);
+ Assembly coreAssembly = typeof(object).GetTypeInfo().Assembly;
+ simpleNameToPathMap.Add(coreAssembly.GetName().Name, coreAssembly.Location);
+
+ Assembly systemRuntime = Assembly.Load(new AssemblyName("System.Runtime"));
+ simpleNameToPathMap.Add(systemRuntime.GetName().Name, systemRuntime.Location);
+
+ var resolver = new TestResolver(simpleNameToPathMap);
+ var typeSystemContext = new ILVerifyTypeSystemContext(resolver);
+ typeSystemContext.SetSystemModule(typeSystemContext.GetModule(resolver.Resolve(coreAssembly.GetName())));
- typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(coreAssembly.GetName().Name));
- return typeSystemContext.GetModuleFromPath(TESTASSEMBLYPATH + assemblyName);
+ return typeSystemContext.GetModule(resolver.Resolve(new AssemblyName(Path.GetFileNameWithoutExtension(assemblyName))));
+ }
+
+ private sealed class TestResolver : ResolverBase
+ {
+ Dictionary<string, string> _simpleNameToPathMap;
+ public TestResolver(Dictionary<string, string> simpleNameToPathMap)
+ {
+ _simpleNameToPathMap = simpleNameToPathMap;
+ }
+
+ protected override PEReader ResolveCore(AssemblyName name)
+ {
+ if (_simpleNameToPathMap.TryGetValue(name.Name, out string path))
+ {
+ return new PEReader(File.OpenRead(path));
+ }
+
+ return null;
+ }
}
}
diff --git a/src/ILVerify/ILVerify.sln b/src/ILVerify/ILVerify.sln
index a26a9be5c..ce368247d 100644
--- a/src/ILVerify/ILVerify.sln
+++ b/src/ILVerify/ILVerify.sln
@@ -5,7 +5,9 @@ VisualStudioVersion = 15.0.26510.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerify", "src\ILVerify.csproj", "{56AA4730-39A4-4B48-95E9-89E8A29F0A06}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerify.Tests", "tests\ILVerify.Tests.csproj", "{1228E4B6-E5E5-414A-94EC-69B792984FAB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerification", "..\ILVerification\src\ILVerification.csproj", "{6166B258-3D41-4431-88D9-510FAF5E6927}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerification.Tests", "..\ILVerification\tests\ILVerification.Tests.csproj", "{894F0BC9-31D1-42D0-9C3F-4FC9A41CD07E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,12 +19,19 @@ Global
{56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Release|Any CPU.Build.0 = Release|Any CPU
- {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6166B258-3D41-4431-88D9-510FAF5E6927}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6166B258-3D41-4431-88D9-510FAF5E6927}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6166B258-3D41-4431-88D9-510FAF5E6927}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6166B258-3D41-4431-88D9-510FAF5E6927}.Release|Any CPU.Build.0 = Release|Any CPU
+ {894F0BC9-31D1-42D0-9C3F-4FC9A41CD07E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {894F0BC9-31D1-42D0-9C3F-4FC9A41CD07E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {894F0BC9-31D1-42D0-9C3F-4FC9A41CD07E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {894F0BC9-31D1-42D0-9C3F-4FC9A41CD07E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {3B6C08A7-1F82-4EC0-9CA7-1F07716F36B1}
+ EndGlobalSection
EndGlobal
diff --git a/src/ILVerify/README.md b/src/ILVerify/README.md
index 662bf8ca2..ef4d0d81f 100644
--- a/src/ILVerify/README.md
+++ b/src/ILVerify/README.md
@@ -14,16 +14,20 @@ Historically on Full Framework IL generators used PEVerify to make sure that the
- No coupling with CoreLib: ILVerify can point to any assembly and verify it. This also includes the full framework base assemblies (especially mscorlib).
- Cross-platform, Open-Source
- It should be easy to add new verification rules
-- Fast spin up/tear down.
+- Fast spin up/tear down.
## The codebase
-The project targets netcoreapp2.0 and uses the new .csproj based project format. If you want to open and compile it with Visual Studio then you need a version, which supports .NET Core 2.0 tooling. This is supported in Visual Studio 2017 Update 3 (Version 15.3) or later. The other option is to use command (with .NET Core 2.0 tooling).
+The project targets netcoreapp2.0 and uses the new .csproj based project format. If you want to open and compile it with Visual Studio then you need a version, which supports .NET Core 2.0 tooling. This is supported in Visual Studio 2017 Update 3 (Version 15.3) or later. The other option is to use command (with .NET Core 2.0 tooling).
+The code is split into three projects:
+- ILVerification is the library with the core verification logic,
+- ILVerification.Tests contains the tests for ILVerification,
+- ILVerify is an application that provides a command-line interface on top of ILVerification.
## Tests
-To test ILVerify we have small methods checked in as .il files testing specific verification scenarios. These tests live under [src/ILVerify/tests/ILTests](https://github.com/dotnet/corert/tree/master/src/ILVerify/tests/ILTests). Tests are grouped into .il files based on functionalities they test. There is no strict policy here, the goal is to have a few dozen .il files instead of thousands containing each only a single method.
+To test the ILVerification library we have small methods checked in as .il files testing specific verification scenarios. These tests live under [src/ILVerification/tests/ILTests](../ILVerification/tests/ILTests). Tests are grouped into .il files based on functionalities they test. There is no strict policy here, the goal is to have a few dozen .il files instead of thousands containing each only a single method.
-Currently the IL files are NOT compiled automatically. You have to compile manually (We want to automatize this step later):
+Currently the IL files are NOT compiled automatically. You have to compile manually (We want to automate this step later):
```
ilasm [filename.il] /dll /ERROR
@@ -35,7 +39,7 @@ Note: if you run the tests and get an error similar to this then it means that t
Result Message: System.InvalidOperationException : No data found for ILVerify.Tests.ILMethodTester.TestMethodsWithInvalidIL
```
-The test project itself is under [src/ILVerify/tests](https://github.com/dotnet/corert/tree/master/src/ILVerify/tests)
+The test project itself is under [src/ILVerification/tests](../ILVerification/tests)
Method names in the .il files must follow the following naming convention:
@@ -44,9 +48,9 @@ The test project itself is under [src/ILVerify/tests](https://github.com/dotnet/
```
[FriendlyName]_Valid
```
-The method must contain 1 '`_`'.
+The method must contain 1 '`_`'.
- The part before the `_` is a friendly name describing what the method does.
- - The word after the `_` must be 'Valid' (Case sensitive)
+ - The word after the `_` must be 'Valid' (Case sensitive)
E.g.: ```SimpleAdd_Valid```
@@ -58,10 +62,10 @@ E.g.: ```SimpleAdd_Valid```
The method name must contain 2 '`_`' characters.
1. part: a friendly name
2. part: must be the word 'Invalid' (Case sensitive)
- 3. part: the expected [VerifierErrors](https://github.com/dotnet/corert/blob/master/src/ILVerify/src/VerifierError.cs) as string separated by '.'. We assert on these errors; the test fails if ILVerify does not report these errors.
-
+ 3. part: the expected [VerifierErrors](../ILVerification/src/VerifierError.cs) as string separated by '.'. We assert on these errors; the test fails if ILVerify does not report these errors.
+
E.g.: ```SimpleAdd_Invalid_ExpectedNumericType```
-
+
### Methods with special names:
In order to test methods with special names (e.g. '.ctor'), the specialname method is defined as usual and a separate empty method is added to the type:
@@ -92,7 +96,7 @@ Currently every IL command falls into one of these categories:
- Not implemented: the implementation is completely missing. The easiest way is to pick one of them (look for NotImplentedException in the code) and implement it. First you should 100% understand the spec. (see [ECMA-335](https://www.ecma-international.org/publications/standards/Ecma-335.htm)), then try to port an existing implementation (sources below).
- Partially implemented: These are typically methods with TODOs in it. As the first phase we want to make sure that for every command the stack is correctly maintained, therefore for some commands we either have no verification or we have only a not complete verification. You can also pick one of these and finish it.
- - Implemented: find and fix bugs ;) .
+ - Implemented: find and fix bugs ;) .
Another option to contribute is to write tests (see Tests section).
@@ -100,4 +104,4 @@ Useful sources:
- [PEVerify source code](https://github.com/lewischeng-ms/sscli/blob/master/clr/src/jit64/newverify.cpp)
- [RyuJIT source code](https://github.com/dotnet/coreclr/blob/master/src/jit), specifically: [exception handling specific part](https://github.com/dotnet/coreclr/blob/master/src/jit/jiteh.cpp), [importer.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp) (look for `Compiler::ver`, `Verify`, `VerifyOrReturn`, and `VerifyOrReturnSpeculative`), [_typeinfo.h](https://github.com/dotnet/coreclr/blob/master/src/jit/_typeinfo.h), [typeinfo.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/typeinfo.cpp)
- [ECMA-335 standard](https://www.ecma-international.org/publications/standards/Ecma-335.htm)
- - [Expert .NET 2.0 IL Assembler book](http://www.apress.com/us/book/9781590596463) by Serge Lidin
+ - [Expert .NET 2.0 IL Assembler book](http://www.apress.com/us/book/9781590596463) by Serge Lidin
diff --git a/src/ILVerify/netcoreapp/ILVerify.cs b/src/ILVerify/netcoreapp/ILVerify.cs
new file mode 100644
index 000000000..6158bb254
--- /dev/null
+++ b/src/ILVerify/netcoreapp/ILVerify.cs
@@ -0,0 +1,9 @@
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ Console.WriteLine("Hello world!");
+ }
+}
diff --git a/src/ILVerify/netcoreapp/ILVerify.csproj b/src/ILVerify/netcoreapp/ILVerify.csproj
new file mode 100644
index 000000000..47b7d3145
--- /dev/null
+++ b/src/ILVerify/netcoreapp/ILVerify.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="System.CommandLine">
+ <Version>0.1.0-e160909-1</Version>
+ </PackageReference>
+ </ItemGroup>
+
+</Project>
diff --git a/src/ILVerify/src/AssemblyInfo.cs b/src/ILVerify/src/AssemblyInfo.cs
index 8e392ff7b..83d0e55a1 100644
--- a/src/ILVerify/src/AssemblyInfo.cs
+++ b/src/ILVerify/src/AssemblyInfo.cs
@@ -1,3 +1,3 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("ILVerify.Tests")]
+[assembly: InternalsVisibleTo("ILVerification.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100314e026e409db99b50d61628136c49095e67f782a3032832cfe1e61ba2af8264d2cf7d9228bdf611c1027f61b0ca4c87ee1c248cd58241a695520ba78e76d1c672c2b597cfa0ab4526dcae2b5b6f36936c126e59ada3500d656f3424826d0dab452ea407039d2846cf0e4820905eee537fe904a86097b5b2f3aaae000fc08fc3")]
diff --git a/src/ILVerify/src/ILVerify.csproj b/src/ILVerify/src/ILVerify.csproj
index 27ce4fc9e..0f5d1e990 100644
--- a/src/ILVerify/src/ILVerify.csproj
+++ b/src/ILVerify/src/ILVerify.csproj
@@ -1,286 +1,23 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
- <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
- <PlatformTarget>AnyCPU</PlatformTarget>
- <CLSCompliant>false</CLSCompliant>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ <RootNamespace>ILVerify</RootNamespace>
+ <AssemblyName>ILVerify</AssemblyName>
+ <AssemblyKey></AssemblyKey>
+ <AssemblyOriginatorKeyFile>..\..\ILVerification\StrongNameKeys\ILVerify.snk</AssemblyOriginatorKeyFile>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <CopyNugetImplementations>false</CopyNugetImplementations>
+ <!-- Force .dll extension even if output type is exe. -->
+ <TargetExt>.dll</TargetExt>
</PropertyGroup>
-
+
<ItemGroup>
- <Compile Include="AssemblyInfo.cs" />
<Compile Include="Program.cs" />
- <Compile Include="ILImporter.Verify.cs" />
- <Compile Include="ILImporter.StackValue.cs" />
- <Compile Include="SimpleArrayOfTRuntimeInterfacesAlgorithm.cs" />
- <Compile Include="SimpleTypeSystemContext.cs" />
- <Compile Include="VerifierError.cs" />
- <Compile Include="TypeSystemHelpers.cs" />
- <Compile Include="InstantiatedGenericParameter.cs" />
- <Compile Include="AccessVerificationHelpers.cs" />
</ItemGroup>
-
<ItemGroup>
- <Compile Include="..\..\Common\src\TypeSystem\CodeGen\MethodDesc.CodeGen.cs">
- <Link>TypeSystem\CodeGen\MethodDesc.CodeGen.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\AlignmentHelper.cs">
- <Link>Utilities\AlignmentHelper.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\CastingHelper.cs">
- <Link>TypeSystem\Common\CastingHelper.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\FunctionPointerType.cs">
- <Link>TypeSystem\Common\FunctionPointerType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\IAssemblyDesc.cs">
- <Link>TypeSystem\Common\IAssemblyDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Instantiation.cs">
- <Link>TypeSystem\Common\Instantiation.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ModuleDesc.cs">
- <Link>TypeSystem\Common\ModuleDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemEntity.cs">
- <Link>TypeSystem\Common\TypeSystemEntity.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemException.cs">
- <Link>TypeSystem\Common\TypeSystemException.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\CustomAttributeTypeNameParser.cs">
- <Link>Utilities\CustomAttributeTypeNameParser.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\LockFreeReaderHashtable.cs">
- <Link>Utilities\LockFreeReaderHashtable.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ArrayType.cs">
- <Link>TypeSystem\Common\ArrayType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ArrayOfTRuntimeInterfacesAlgorithm.cs">
- <Link>TypeSystem\Common\ArrayOfTRuntimeInterfacesAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\BaseTypeRuntimeInterfacesAlgorithm.cs">
- <Link>TypeSystem\Common\BaseTypeRuntimeInterfacesAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ByRefType.cs">
- <Link>TypeSystem\Common\ByRefType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\GenericParameterDesc.cs">
- <Link>TypeSystem\Common\GenericParameterDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ExceptionStringID.cs">
- <Link>TypeSystem\Common\ExceptionStringID.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\FieldForInstantiatedType.cs">
- <Link>TypeSystem\Common\FieldForInstantiatedType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\FieldDesc.cs">
- <Link>TypeSystem\Common\FieldDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\FieldDesc.FieldLayout.cs">
- <Link>TypeSystem\Common\FieldDesc.FieldLayout.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\FieldLayoutAlgorithm.cs">
- <Link>TypeSystem\Common\FieldLayoutAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedMethod.cs">
- <Link>TypeSystem\Common\InstantiatedMethod.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.cs">
- <Link>TypeSystem\Common\InstantiatedType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.Interfaces.cs">
- <Link>TypeSystem\Common\InstantiatedType.Interfaces.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\InstantiatedType.MethodImpls.cs">
- <Link>TypeSystem\Common\InstantiatedType.MethodImpls.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\LayoutInt.cs">
- <Link>TypeSystem\Common\LayoutInt.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.cs">
- <Link>TypeSystem\Common\MetadataType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.Interfaces.cs">
- <Link>TypeSystem\Common\MetadataType.Interfaces.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataType.MethodImpls.cs">
- <Link>TypeSystem\Common\MetadataType.MethodImpls.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataFieldLayoutAlgorithm.cs">
- <Link>TypeSystem\Common\MetadataFieldLayoutAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataRuntimeInterfacesAlgorithm.cs">
- <Link>TypeSystem\Common\MetadataRuntimeInterfacesAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataTypeSystemContext.cs">
- <Link>TypeSystem\Common\MetadataTypeSystemContext.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MethodForInstantiatedType.cs">
- <Link>TypeSystem\Common\MethodForInstantiatedType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ParameterizedType.cs">
- <Link>TypeSystem\Common\ParameterizedType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\PointerType.cs">
- <Link>TypeSystem\Common\PointerType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\PropertySignature.cs">
- <Link>TypeSystem\Common\PropertySignature.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\SignatureVariable.cs">
- <Link>TypeSystem\Common\SignatureVariable.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TargetDetails.cs">
- <Link>TypeSystem\Common\TargetDetails.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ThreadSafeFlags.cs">
- <Link>TypeSystem\Common\ThreadSafeFlags.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeFlags.cs">
- <Link>TypeSystem\Common\TypeFlags.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeHashingAlgorithms.cs">
- <Link>TypeSystem\Common\TypeHashingAlgorithms.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemContext.cs">
- <Link>TypeSystem\Common\TypeSystemContext.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemHelpers.cs">
- <Link>TypeSystem\Common\TypeSystemHelpers.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\TypeNameFormatter.cs">
- <Link>Utilities\TypeNameFormatter.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\WellKnownType.cs">
- <Link>TypeSystem\Common\WellKnownType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\VirtualMethodAlgorithm.cs">
- <Link>TypeSystem\Common\VirtualMethodAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MethodDesc.cs">
- <Link>TypeSystem\Common\MethodDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MetadataVirtualMethodAlgorithm.cs">
- <Link>TypeSystem\Common\StandardVirtualMethodAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeDesc.cs">
- <Link>TypeSystem\Common\TypeDesc.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeDesc.Interfaces.cs">
- <Link>TypeSystem\Common\TypeDesc.Interfaces.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\DefType.cs">
- <Link>TypeSystem\Common\DefType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\DefType.FieldLayout.cs">
- <Link>TypeSystem\Common\DefType.FieldLayout.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\RuntimeInterfacesAlgorithm.cs">
- <Link>TypeSystem\Common\RuntimeInterfacesAlgorithm.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ThrowHelper.Common.cs">
- <Link>TypeSystem\Common\ThrowHelper.Common.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\ThrowHelper.cs">
- <Link>TypeSystem\Common\ThrowHelper.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs">
- <Link>TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.Metadata.cs">
- <Link>TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.Metadata.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\CustomAttributeTypeProvider.cs">
- <Link>Ecma\CustomAttributeTypeProvider.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaAssembly.cs">
- <Link>Ecma\EcmaAssembly.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaField.cs">
- <Link>Ecma\EcmaField.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaGenericParameter.cs">
- <Link>Ecma\EcmaGenericParameter.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaMethod.cs">
- <Link>Ecma\EcmaMethod.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaModule.cs">
- <Link>Ecma\EcmaModule.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaSignatureParser.cs">
- <Link>Ecma\EcmaSignatureParser.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.cs">
- <Link>Ecma\EcmaType.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.MethodImpls.cs">
- <Link>Ecma\EcmaType.MethodImpls.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\EcmaType.Interfaces.cs">
- <Link>Ecma\EcmaType.Interfaces.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\MetadataExtensions.cs">
- <Link>Ecma\MetadataExtensions.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\IMetadataStringDecoderProvider.cs">
- <Link>Ecma\IMetadataStringDecoderProvider.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\CachingMetadataStringDecoder.cs">
- <Link>Ecma\CachingMetadataStringDecoder.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Ecma\PrimitiveTypeProvider.cs">
- <Link>Ecma\PrimitiveTypeProvider.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\EcmaMethodIL.cs">
- <Link>IL\EcmaMethodIL.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\MethodIL.cs">
- <Link>IL\MethodIL.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\MethodILDebugView.cs">
- <Link>IL\MethodILDebugView.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\ILDisassembler.cs">
- <Link>IL\ILDisassembler.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\InstantiatedMethodIL.cs">
- <Link>IL\InstantiatedMethodIL.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcode.cs">
- <Link>IL\ILOpcode.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\IL\ILImporter.cs">
- <Link>IL\ILImporter.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Interop\InstantiatedType.Interop.cs">
- <Link>Interop\InstantiatedType.Interop.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Interop\MetadataType.Interop.cs">
- <Link>Interop\MetadataType.Interop.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Interop\MethodDesc.Interop.cs">
- <Link>Interop\MethodDesc.Interop.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Interop\MarshalAsDescriptor.cs">
- <Link>TypeSystem\Interop\MarshalAsDescriptor.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\System\Collections\Generic\ArrayBuilder.cs">
- <Link>Utilities\ArrayBuilder.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\LocalVariableDefinition.cs">
- <Link>TypeSystem\Common\LocalVariableDefinition.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\System\FormattingHelpers.cs">
- <Link>Common\System\FormattingHelpers.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemConstraintsHelpers.cs">
- <Link>TypeSystem\Common\TypeSystemConstraintsHelpers.cs</Link>
- </Compile>
+ <PackageReference Include="System.CommandLine" Version="0.1.0-e160909-1" />
+ <ProjectReference Include="..\..\ILVerification\src\ILVerification.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Common\src\CommandLine\CommandLineException.cs">
@@ -295,8 +32,20 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="System.IO.MemoryMappedFiles" Version="4.3.0" />
- <PackageReference Include="System.Reflection.Metadata" Version="1.4.1" />
- <PackageReference Include="System.CommandLine" Version="0.1.0-e160909-1" />
+ <None Include="..\..\ILVerification\StrongNameKeys\ILVerify.snk" Link="ILVerify.snk" />
</ItemGroup>
-</Project> \ No newline at end of file
+
+ <ItemGroup>
+ <Content Include="ILVerify.runtimeconfig.json">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+
+ <PropertyGroup>
+ <RunCommand Condition="'$(OS)' == 'Windows_NT'">$(TargetDir)$(AssemblyName).exe</RunCommand>
+ <RunCommand Condition="'$(OS)' != 'Windows_NT'">$(TargetDir)$(AssemblyName)</RunCommand>
+ <RunArguments>$(StartArguments)</RunArguments>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/ILVerify/src/ILVerify.runtimeconfig.json b/src/ILVerify/src/ILVerify.runtimeconfig.json
new file mode 100644
index 000000000..76e177076
--- /dev/null
+++ b/src/ILVerify/src/ILVerify.runtimeconfig.json
@@ -0,0 +1,7 @@
+{
+ "runtimeOptions": {
+ "configProperties": {
+ "Microsoft.NETCore.DotNetHostPolicy.SetAppPaths": true
+ }
+ }
+}
diff --git a/src/ILVerify/src/Program.cs b/src/ILVerify/src/Program.cs
index aec3b5c18..d748084b8 100644
--- a/src/ILVerify/src/Program.cs
+++ b/src/ILVerify/src/Program.cs
@@ -3,42 +3,31 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.IO;
using System.Collections.Generic;
using System.CommandLine;
-using System.Reflection;
+using System.IO;
using System.Linq;
+using System.Reflection;
using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
-using System.Text;
-
-using Internal.TypeSystem;
-using Internal.TypeSystem.Ecma;
-using Internal.IL;
-
-using Internal.CommandLine;
using System.Text.RegularExpressions;
-using System.Globalization;
-using System.Resources;
+using Internal.CommandLine;
+using Internal.TypeSystem.Ecma;
+using static System.Console;
namespace ILVerify
{
- class Program
+ class Program : ResolverBase
{
- private const string DefaultSystemModuleName = "mscorlib";
private bool _help;
- private string _systemModule = DefaultSystemModuleName;
- private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ private AssemblyName _systemModule = new AssemblyName("mscorlib");
+ private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // map of simple name to file path
+ private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // map of simple name to file path
private IReadOnlyList<Regex> _includePatterns = Array.Empty<Regex>();
private IReadOnlyList<Regex> _excludePatterns = Array.Empty<Regex>();
- private SimpleTypeSystemContext _typeSystemContext;
- private ResourceManager _stringResourceManager;
-
- private int _numErrors;
+ private Verifier _verifier;
private Program()
{
@@ -46,9 +35,9 @@ namespace ILVerify
private void Help(string helpText)
{
- Console.WriteLine("ILVerify version " + typeof(Program).GetTypeInfo().Assembly.GetName().Version.ToString());
- Console.WriteLine();
- Console.WriteLine(helpText);
+ WriteLine("ILVerify version " + typeof(Program).GetTypeInfo().Assembly.GetName().Version.ToString());
+ WriteLine();
+ WriteLine(helpText);
}
public static IReadOnlyList<Regex> StringPatternsToRegexList(IReadOnlyList<string> patterns)
@@ -78,7 +67,7 @@ namespace ILVerify
syntax.HandleErrors = true;
syntax.DefineOption("h|help", ref _help, "Display this usage message");
- syntax.DefineOption("s|system-module", ref _systemModule, "System module name (default: mscorlib)");
+ syntax.DefineOption("s|system-module", ref _systemModule, s => new AssemblyName(s), "System module name (default: mscorlib)");
syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference metadata from the specified assembly");
syntax.DefineOptionList("i|include", ref includePatterns, "Use only methods/types/namespaces, which match the given regular expression(s)");
syntax.DefineOption("include-file", ref includeFile, "Same as --include, but the regular expression(s) are declared line by line in the specified file.");
@@ -97,7 +86,7 @@ namespace ILVerify
if (!string.IsNullOrEmpty(includeFile))
{
if (includePatterns.Count > 0)
- Console.WriteLine("[Warning] --include-file takes precedence over --include");
+ WriteLine("[Warning] --include-file takes precedence over --include");
includePatterns = File.ReadAllLines(includeFile);
}
_includePatterns = StringPatternsToRegexList(includePatterns);
@@ -105,7 +94,7 @@ namespace ILVerify
if (!string.IsNullOrEmpty(excludeFile))
{
if (excludePatterns.Count > 0)
- Console.WriteLine("[Warning] --exclude-file takes precedence over --exclude");
+ WriteLine("[Warning] --exclude-file takes precedence over --exclude");
excludePatterns = File.ReadAllLines(excludeFile);
}
_excludePatterns = StringPatternsToRegexList(excludePatterns);
@@ -113,153 +102,172 @@ namespace ILVerify
return argSyntax;
}
- private void VerifyMethod(MethodDesc method, MethodIL methodIL)
+ private int Run(string[] args)
{
- // Console.WriteLine("Verifying: " + method.ToString());
+ ArgumentSyntax syntax = ParseCommandLine(args);
+ if (_help)
+ {
+ Help(syntax.GetHelpText());
+ return 1;
+ }
- try
+ if (_inputFilePaths.Count == 0)
+ throw new CommandLineException("No input files specified");
+
+ _verifier = new Verifier(this);
+ _verifier.SetSystemModuleName(_systemModule);
+
+ foreach (var kvp in _inputFilePaths)
{
- var importer = new ILImporter(method, methodIL);
+ var results = VerifyAssembly(new AssemblyName(kvp.Key), out EcmaModule module);
+ int numErrors = 0;
- importer.ReportVerificationError = (args) =>
+ foreach (var result in results)
{
- var message = new StringBuilder();
-
- message.Append("[IL]: Error: ");
-
- message.Append("[");
- message.Append(_typeSystemContext.GetModulePath(((EcmaMethod)method).Module));
- message.Append(" : ");
- message.Append(((EcmaType)method.OwningType).Name);
- message.Append("::");
- message.Append(method.Name);
- message.Append("(");
- if (method.Signature._parameters != null && method.Signature._parameters.Length > 0)
- {
- foreach (TypeDesc parameter in method.Signature._parameters)
- {
- message.Append(parameter.ToString());
- message.Append(", ");
- }
- message.Remove(message.Length - 2, 2);
- }
- message.Append(")");
- message.Append("]");
+ numErrors++;
+ PrintResult(result, module, kvp.Value);
+ }
- message.Append("[offset 0x");
- message.Append(args.Offset.ToString("X8"));
- message.Append("]");
+ if (numErrors > 0)
+ WriteLine(numErrors + " Error(s) Verifying " + kvp.Value);
+ else
+ WriteLine("All Classes and Methods in " + kvp.Value + " Verified.");
+ }
- if (args.Found != null)
- {
- message.Append("[found ");
- message.Append(args.Found);
- message.Append("]");
- }
+ return 0;
+ }
- if (args.Expected != null)
- {
- message.Append("[expected ");
- message.Append(args.Expected);
- message.Append("]");
- }
+ private void PrintResult(VerificationResult result, EcmaModule module, string pathOrModuleName)
+ {
+ Write("[IL]: Error: ");
- if (args.Token != 0)
- {
- message.Append("[token 0x");
- message.Append(args.Token.ToString("X8"));
- message.Append("]");
- }
+ Write("[");
+ Write(pathOrModuleName);
+ Write(" : ");
- message.Append(" ");
+ MetadataReader metadataReader = module.MetadataReader;
- if (_stringResourceManager == null)
- {
- _stringResourceManager = new ResourceManager("ILVerify.Resources.Strings", Assembly.GetExecutingAssembly());
- }
+ TypeDefinition typeDef = metadataReader.GetTypeDefinition(metadataReader.GetMethodDefinition(result.Method).GetDeclaringType());
+ string typeName = metadataReader.GetString(typeDef.Name);
+ Write(typeName);
- var str = _stringResourceManager.GetString(args.Code.ToString(), CultureInfo.InvariantCulture);
- message.Append(string.IsNullOrEmpty(str) ? args.Code.ToString() : str);
+ Write("::");
+ var method = (EcmaMethod)module.GetMethod(result.Method);
+ PrintMethod(method);
+ Write("]");
- Console.WriteLine(message);
+ var args = result.Error;
+ if (args.Code != VerifierError.None)
+ {
+ Write("[offset 0x");
+ Write(args.Offset.ToString("X8"));
+ Write("]");
- _numErrors++;
- };
+ if (args.Found != null)
+ {
+ Write("[found ");
+ Write(args.Found);
+ Write("]");
+ }
- importer.Verify();
- }
- catch (NotImplementedException e)
- {
- Console.Error.WriteLine($"Error in {method}: {e.Message}");
- }
- catch (InvalidProgramException e)
- {
- Console.Error.WriteLine($"Error in {method}: {e.Message}");
- }
- catch (VerificationException)
- {
- }
- catch (BadImageFormatException)
- {
- Console.WriteLine("Unable to resolve token");
- }
- catch (PlatformNotSupportedException e)
- {
- Console.WriteLine(e.Message);
+ if (args.Expected != null)
+ {
+ Write("[expected ");
+ Write(args.Expected);
+ Write("]");
+ }
+
+ if (args.Token != 0)
+ {
+ Write("[token 0x");
+ Write(args.Token.ToString("X8"));
+ Write("]");
+ }
}
+
+ Write(" ");
+ WriteLine(result.Message);
}
- private void VerifyModule(EcmaModule module)
+ private static void PrintMethod(EcmaMethod method)
{
- foreach (var methodHandle in module.MetadataReader.MethodDefinitions)
+ Write(method.Name);
+ Write("(");
+
+ if (method.Signature.Length > 0)
{
- var method = (EcmaMethod)module.GetMethod(methodHandle);
+ bool first = true;
+ for(int i = 0; i < method.Signature.Length; i++)
+ {
+ Internal.TypeSystem.TypeDesc parameter = method.Signature[0];
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Write(", ");
+ }
- var methodIL = EcmaMethodIL.Create(method);
- if (methodIL == null)
- continue;
+ Write(parameter.ToString());
+ }
+ }
- var methodName = method.ToString();
- if (_includePatterns.Count > 0 && !_includePatterns.Any(p => p.IsMatch(methodName)))
- continue;
- if (_excludePatterns.Any(p => p.IsMatch(methodName)))
- continue;
+ Write(")");
+ }
- VerifyMethod(method, methodIL);
+ private IEnumerable<VerificationResult> VerifyAssembly(AssemblyName name, out EcmaModule module)
+ {
+ PEReader peReader = Resolve(name);
+ module = _verifier.GetModule(peReader);
+
+ return VerifyAssembly(peReader);
+ }
+
+ private IEnumerable<VerificationResult> VerifyAssembly(PEReader peReader)
+ {
+ MetadataReader metadataReader = peReader.GetMetadataReader();
+ foreach (var methodHandle in metadataReader.MethodDefinitions)
+ {
+ var methodName = metadataReader.GetString(metadataReader.GetMethodDefinition(methodHandle).Name);
+ if (ShouldVerifyMethod(methodName))
+ {
+ var results = _verifier.Verify(peReader, methodHandle);
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
}
}
- private int Run(string[] args)
+ private bool ShouldVerifyMethod(string methodName)
{
- ArgumentSyntax syntax = ParseCommandLine(args);
- if (_help)
+ if (_includePatterns.Count > 0 && !_includePatterns.Any(p => p.IsMatch(methodName)))
{
- Help(syntax.GetHelpText());
- return 1;
+ return false;
}
- if (_inputFilePaths.Count == 0)
- throw new CommandLineException("No input files specified");
+ if (_excludePatterns.Any(p => p.IsMatch(methodName)))
+ {
+ return false;
+ }
- _typeSystemContext = new SimpleTypeSystemContext();
- _typeSystemContext.InputFilePaths = _inputFilePaths;
- _typeSystemContext.ReferenceFilePaths = _referenceFilePaths;
+ return true;
+ }
- _typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(_systemModule));
+ protected override PEReader ResolveCore(AssemblyName name)
+ {
+ // Note: we use simple names instead of full names to resolve, because we can't get a full name from an assembly without reading it
+ string simpleName = name.Name;
- foreach (var inputPath in _inputFilePaths.Values)
+ string path = null;
+ if (_inputFilePaths.TryGetValue(simpleName, out path) || _referenceFilePaths.TryGetValue(simpleName, out path))
{
- _numErrors = 0;
-
- VerifyModule(_typeSystemContext.GetModuleFromPath(inputPath));
-
- if (_numErrors > 0)
- Console.WriteLine(_numErrors + " Error(s) Verifying " + inputPath);
- else
- Console.WriteLine("All Classes and Methods in " + inputPath + " Verified.");
+ return new PEReader(File.OpenRead(path));
}
- return 0;
+ return null;
}
private static int Main(string[] args)
diff --git a/src/ILVerify/src/SimpleTypeSystemContext.cs b/src/ILVerify/src/SimpleTypeSystemContext.cs
deleted file mode 100644
index 123e2e0d2..000000000
--- a/src/ILVerify/src/SimpleTypeSystemContext.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
-using System.Reflection.PortableExecutable;
-
-using Internal.TypeSystem;
-using Internal.TypeSystem.Ecma;
-
-using Internal.CommandLine;
-
-namespace ILVerify
-{
- class SimpleTypeSystemContext : MetadataTypeSystemContext
- {
- private RuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
- private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm();
-
- Dictionary<string, EcmaModule> _modules = new Dictionary<string, EcmaModule>(StringComparer.OrdinalIgnoreCase);
-
- class ModuleData
- {
- public string Path;
- }
- Dictionary<EcmaModule, ModuleData> _moduleData = new Dictionary<EcmaModule, ModuleData>();
-
- public SimpleTypeSystemContext()
- {
- }
-
- public IDictionary<string, string> InputFilePaths
- {
- get;
- set;
- }
-
- public IDictionary<string, string> ReferenceFilePaths
- {
- get;
- set;
- }
-
- public override ModuleDesc ResolveAssembly(AssemblyName name, bool throwIfNotFound = true)
- {
- return GetModuleForSimpleName(name.Name);
- }
-
- public EcmaModule GetModuleForSimpleName(string simpleName)
- {
- EcmaModule existingModule;
- if (_modules.TryGetValue(simpleName, out existingModule))
- return existingModule;
-
- return CreateModuleForSimpleName(simpleName);
- }
-
- private EcmaModule CreateModuleForSimpleName(string simpleName)
- {
- string filePath;
- if (!InputFilePaths.TryGetValue(simpleName, out filePath))
- {
- if (!ReferenceFilePaths.TryGetValue(simpleName, out filePath))
- throw new CommandLineException("Assembly not found: " + simpleName);
- }
-
- PEReader peReader = new PEReader(File.OpenRead(filePath));
- EcmaModule module = EcmaModule.Create(this, peReader);
-
- MetadataReader metadataReader = module.MetadataReader;
- string actualSimpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (!actualSimpleName.Equals(simpleName, StringComparison.OrdinalIgnoreCase))
- throw new CommandLineException("Assembly name does not match filename " + filePath);
-
- _modules.Add(simpleName, module);
-
- ModuleData moduleData = new ModuleData() { Path = filePath };
- _moduleData.Add(module, moduleData);
-
- return module;
- }
-
- public EcmaModule GetModuleFromPath(string filePath)
- {
- // This is called once for every assembly that should be verified, so linear search is acceptable.
- foreach (KeyValuePair<EcmaModule, ModuleData> entry in _moduleData)
- {
- EcmaModule curModule = entry.Key;
- ModuleData curData = entry.Value;
- if (curData.Path == filePath)
- return curModule;
- }
-
- PEReader peReader = new PEReader(File.OpenRead(filePath));
- EcmaModule module = EcmaModule.Create(this, peReader);
-
- MetadataReader metadataReader = module.MetadataReader;
- string simpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (_modules.ContainsKey(simpleName))
- throw new CommandLineException("Module with same simple name already exists " + filePath);
-
- _modules.Add(simpleName, module);
-
- ModuleData moduleData = new ModuleData() { Path = filePath };
- _moduleData.Add(module, moduleData);
-
- return module;
- }
-
- public string GetModulePath(EcmaModule module)
- {
- return _moduleData[module].Path;
- }
-
- protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type)
- {
- if (_arrayOfTRuntimeInterfacesAlgorithm == null)
- {
- _arrayOfTRuntimeInterfacesAlgorithm = new SimpleArrayOfTRuntimeInterfacesAlgorithm(SystemModule);
- }
- return _arrayOfTRuntimeInterfacesAlgorithm;
- }
-
- protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type)
- {
- return _metadataRuntimeInterfacesAlgorithm;
- }
- }
-}
diff --git a/src/ILVerify/tests/ILVerify.Tests.csproj b/src/ILVerify/tests/ILVerify.Tests.csproj
deleted file mode 100644
index 94c21cd15..000000000
--- a/src/ILVerify/tests/ILVerify.Tests.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>netcoreapp2.0</TargetFramework>
-
- <IsPackable>false</IsPackable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170427-09" />
- <PackageReference Include="xunit" Version="2.2.0" />
- <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\src\ILVerify.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
- </ItemGroup>
-
- <ItemGroup>
- <Folder Include="ILTests\" />
- </ItemGroup>
-
-</Project>
diff --git a/src/JitInterface/src/CorInfoHelpFunc.cs b/src/JitInterface/src/CorInfoHelpFunc.cs
index 392f59f96..ababdf60e 100644
--- a/src/JitInterface/src/CorInfoHelpFunc.cs
+++ b/src/JitInterface/src/CorInfoHelpFunc.cs
@@ -300,6 +300,7 @@ namespace Internal.JitInterface
CORINFO_HELP_THROW_ARGUMENTEXCEPTION, // throw ArgumentException
CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, // throw ArgumentOutOfRangeException
CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, // throw PlatformNotSupportedException
+ CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, // throw TypeNotSupportedException
CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument
CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument
diff --git a/src/JitInterface/src/CorInfoImpl.Intrinsics.cs b/src/JitInterface/src/CorInfoImpl.Intrinsics.cs
index 18bb742dd..888957b32 100644
--- a/src/JitInterface/src/CorInfoImpl.Intrinsics.cs
+++ b/src/JitInterface/src/CorInfoImpl.Intrinsics.cs
@@ -19,8 +19,8 @@ namespace Internal.JitInterface
public bool Equals(IntrinsicKey other)
{
- return (MethodName == other.MethodName) &&
- (TypeNamespace == other.TypeNamespace) &&
+ return (MethodName == other.MethodName) &&
+ (TypeNamespace == other.TypeNamespace) &&
(TypeName == other.TypeName);
}
@@ -78,27 +78,49 @@ namespace Internal.JitInterface
IntrinsicHashtable table = new IntrinsicHashtable();
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sin, "Sin", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sin, "Sin", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cos, "Cos", "System", "Math");
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cbrt, "Cbrt", "System", "Math"); // not in CoreRT yet
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cos, "Cos", "System", "MathF");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cbrt, "Cbrt", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cbrt, "Cbrt", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sqrt, "Sqrt", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sqrt, "Sqrt", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Abs, "Abs", "System", "Math");
+ // No System.MathF entry for CORINFO_INTRTINSIC_Abs as System.Math exposes and handles both float and double
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Round, "Round", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Round, "Round", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cosh, "Cosh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cosh, "Cosh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sinh, "Sinh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sinh, "Sinh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tan, "Tan", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tan, "Tan", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tanh, "Tanh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tanh, "Tanh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asin, "Asin", "System", "Math");
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asinh, "Asinh", "System", "Math"); // not in CoreRT yet
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asin, "Asin", "System", "MathF");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asinh, "Asinh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asinh, "Asinh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acos, "Acos", "System", "Math");
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acosh, "Acosh", "System", "Math"); // not in CoreRT yet
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acos, "Acos", "System", "MathF");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acosh, "Acosh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acosh, "Acosh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan, "Atan", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan, "Atan", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan2, "Atan2", "System", "Math");
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atanh, "Atanh", "System", "Math"); // not in CoreRT yet
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan2, "Atan2", "System", "MathF");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atanh, "Atanh", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atanh, "Atanh", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Log10, "Log10", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Log10, "Log10", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Pow, "Pow", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Pow, "Pow", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Exp, "Exp", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Exp, "Exp", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Ceiling, "Ceiling", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Ceiling, "Ceiling", "System", "MathF");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Floor, "Floor", "System", "Math");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Floor, "Floor", "System", "MathF");
// table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetChar, null, null, null); // unused
// table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Array_GetDimLength, "GetLength", "System", "Array"); // not handled
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Array_Get, "Get", null, null);
@@ -109,8 +131,8 @@ namespace Internal.JitInterface
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InitializeArray, "InitializeArray", "System.Runtime.CompilerServices", "RuntimeHelpers");
//table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetTypeFromHandle, "GetTypeFromHandle", "System", "Type"); // RuntimeTypeHandle has to be RuntimeType
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_RTH_GetValueInternal, "GetValueInternal", "System", "RuntimeTypeHandle");
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_TypeEQ, "op_Equality", "System", "Type"); // not in .NET Core
- // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_TypeNEQ, "op_Inequality", "System", "Type"); // not in .NET Core
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_TypeEQ, "op_Equality", "System", "Type");
+ table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_TypeNEQ, "op_Inequality", "System", "Type");
table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Object_GetType, "GetType", "System", "Object");
// table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContext, "GetStubContext", "System.StubHelpers", "StubHelpers"); // interop-specific
// table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, "GetStubContextAddr", "System.StubHelpers", "StubHelpers"); // interop-specific
diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs
index c06bd6b5c..a9eaceaab 100644
--- a/src/JitInterface/src/CorInfoImpl.cs
+++ b/src/JitInterface/src/CorInfoImpl.cs
@@ -2,6 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#if SUPPORT_JIT
+extern alias System_Private_CoreLib;
+using TextWriter = System_Private_CoreLib::System.IO.TextWriter;
+#endif
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -855,7 +860,7 @@ namespace Internal.JitInterface
// Normalize to the slot defining method. We don't have slot information for the overrides.
methodDesc = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(methodDesc);
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc.Normalize());
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc);
Debug.Assert(slot != -1);
offsetAfterIndirection = (uint)(EETypeNode.GetVTableOffset(pointerSize) + slot * pointerSize);
@@ -2899,6 +2904,7 @@ namespace Internal.JitInterface
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_NewObj
|| pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Box
+ || pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Constrained
|| (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldtoken && ConstructedEETypeNode.CreationAllowed(td)))
{
helperId = ReadyToRunHelperId.TypeHandle;
@@ -3299,7 +3305,7 @@ namespace Internal.JitInterface
}
}
else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0
- && _compilation.HasFixedSlotVTable(targetMethod.Normalize().OwningType))
+ && _compilation.HasFixedSlotVTable(targetMethod.OwningType))
{
pResult.kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_VTABLE;
pResult.nullInstanceCheck = true;
diff --git a/src/JitInterface/src/CorInfoTypes.cs b/src/JitInterface/src/CorInfoTypes.cs
index b427f4476..b0ceda613 100644
--- a/src/JitInterface/src/CorInfoTypes.cs
+++ b/src/JitInterface/src/CorInfoTypes.cs
@@ -1464,7 +1464,7 @@ namespace Internal.JitInterface
CORJIT_FLAG_UNUSED3 = 10,
CORJIT_FLAG_UNUSED4 = 11,
CORJIT_FLAG_UNUSED5 = 12,
- CORJIT_FLAG_USE_SSE3_4 = 13,
+ CORJIT_FLAG_UNUSED6 = 13,
CORJIT_FLAG_USE_AVX = 14,
CORJIT_FLAG_USE_AVX2 = 15,
CORJIT_FLAG_USE_AVX_512 = 16,
diff --git a/src/JitInterface/src/ThunkGenerator/corinfo.h b/src/JitInterface/src/ThunkGenerator/corinfo.h
index c79dd3f4c..f3b509c5a 100644
--- a/src/JitInterface/src/ThunkGenerator/corinfo.h
+++ b/src/JitInterface/src/ThunkGenerator/corinfo.h
@@ -213,14 +213,13 @@ TODO: Talk about initializing strutures before use
#define SELECTANY extern __declspec(selectany)
#endif
-SELECTANY const GUID JITEEVersionIdentifier = { /* a6860f80-01cb-4f87-82c2-a8e5a744f2fa */
- 0xa6860f80,
- 0x01cb,
- 0x4f87,
- {0x82, 0xc2, 0xa8, 0xe5, 0xa7, 0x44, 0xf2, 0xfa}
+SELECTANY const GUID JITEEVersionIdentifier = { /* 0ba106c8-81a0-407f-99a1-928448c1eb62 */
+ 0x0ba106c8,
+ 0x81a0,
+ 0x407f,
+ {0x99, 0xa1, 0x92, 0x84, 0x48, 0xc1, 0xeb, 0x62}
};
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// END JITEEVersionIdentifier
@@ -644,6 +643,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_THROW_ARGUMENTEXCEPTION, // throw ArgumentException
CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, // throw ArgumentOutOfRangeException
CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, // throw PlatformNotSupportedException
+ CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, // throw TypeNotSupportedException
CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument
CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument
diff --git a/src/JitInterface/src/ThunkGenerator/corjitflags.h b/src/JitInterface/src/ThunkGenerator/corjitflags.h
index 2598c26ac..da303b6a0 100644
--- a/src/JitInterface/src/ThunkGenerator/corjitflags.h
+++ b/src/JitInterface/src/ThunkGenerator/corjitflags.h
@@ -52,16 +52,16 @@ public:
#endif // !defined(_TARGET_X86_)
+ CORJIT_FLAG_UNUSED6 = 13,
+
#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
- CORJIT_FLAG_USE_SSE3_4 = 13,
CORJIT_FLAG_USE_AVX = 14,
CORJIT_FLAG_USE_AVX2 = 15,
CORJIT_FLAG_USE_AVX_512 = 16,
#else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_)
- CORJIT_FLAG_UNUSED6 = 13,
CORJIT_FLAG_UNUSED7 = 14,
CORJIT_FLAG_UNUSED8 = 15,
CORJIT_FLAG_UNUSED9 = 16,
diff --git a/src/Native/Bootstrap/common.h b/src/Native/Bootstrap/common.h
index 06a1ac5e6..fb804e459 100644
--- a/src/Native/Bootstrap/common.h
+++ b/src/Native/Bootstrap/common.h
@@ -47,6 +47,7 @@ extern "C" Object * __allocate_array(size_t elements, MethodTable * pMT);
extern "C" Object * __castclass(void * obj, MethodTable * pMT);
extern "C" Object * __isinst(void * obj, MethodTable * pMT);
extern "C" __NORETURN void __throw_exception(void * pEx);
+extern "C" void __debug_break();
Object * __load_string_literal(const char * string);
diff --git a/src/Native/Bootstrap/main.cpp b/src/Native/Bootstrap/main.cpp
index 1f482795a..43860640d 100644
--- a/src/Native/Bootstrap/main.cpp
+++ b/src/Native/Bootstrap/main.cpp
@@ -98,6 +98,7 @@ extern "C" void * RhTypeCast_CheckCast(void * pObject, MethodTable * pMT);
extern "C" void RhpStelemRef(void * pArray, int index, void * pObj);
extern "C" void * RhpLdelemaRef(void * pArray, int index, MethodTable * pMT);
extern "C" __NORETURN void RhpThrowEx(void * pEx);
+extern "C" void RhDebugBreak();
extern "C" Object * __allocate_object(MethodTable * pMT)
{
@@ -134,6 +135,11 @@ extern "C" void __throw_exception(void * pEx)
RhpThrowEx(pEx);
}
+extern "C" void __debug_break()
+{
+ RhDebugBreak();
+}
+
void __range_check_fail()
{
throw "ThrowRangeOverflowException";
@@ -288,7 +294,9 @@ static const pfn c_classlibFunctions[] = {
&AppendExceptionStackFrame,
nullptr, // &CheckStaticClassConstruction,
&GetSystemArrayEEType,
- &OnFirstChanceException
+ &OnFirstChanceException,
+ nullptr, // &DebugFuncEvalHelper,
+ nullptr, // &DebugFuncEvalAbortHelper,
};
#endif // !CPPCODEGEN
diff --git a/src/Native/ObjWriter/llvm.patch b/src/Native/ObjWriter/llvm.patch
index 2dde952d5..fd8420125 100644
--- a/src/Native/ObjWriter/llvm.patch
+++ b/src/Native/ObjWriter/llvm.patch
@@ -1,5 +1,5 @@
diff --git a/include/llvm/MC/MCObjectStreamer.h b/include/llvm/MC/MCObjectStreamer.h
-index 7c1189e46ab..d1d77c97311 100644
+index 7c1189e..d1d77c9 100644
--- a/include/llvm/MC/MCObjectStreamer.h
+++ b/include/llvm/MC/MCObjectStreamer.h
@@ -101,6 +101,11 @@ public:
@@ -14,8 +14,20 @@ index 7c1189e46ab..d1d77c97311 100644
/// \brief Emit an instruction to a special fragment, because this instruction
/// can change its size during relaxation.
virtual void EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &);
+diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h
+index 5390e79..e28a3cc 100644
+--- a/include/llvm/MC/MCStreamer.h
++++ b/include/llvm/MC/MCStreamer.h
+@@ -115,6 +115,7 @@ public:
+ virtual void emitPad(int64_t Offset);
+ virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
+ bool isVector);
++ virtual void emitLsda(const MCSymbol *Symbol);
+ virtual void emitUnwindRaw(int64_t StackOffset,
+ const SmallVectorImpl<uint8_t> &Opcodes);
+
diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp
-index 174397e2739..ef7161fb56c 100644
+index 174397e..ef7161f 100644
--- a/lib/MC/MCObjectStreamer.cpp
+++ b/lib/MC/MCObjectStreamer.cpp
@@ -122,7 +122,7 @@ void MCObjectStreamer::EmitCFISections(bool EH, bool Debug) {
@@ -46,7 +58,7 @@ index 174397e2739..ef7161fb56c 100644
// We need to create a local symbol to avoid relocations.
Frame.Begin = getContext().createTempSymbol();
diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
-index a77df7a2598..e1aa7526f9b 100644
+index a77df7a..e1aa752 100644
--- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -48,6 +48,14 @@ public:
@@ -84,7 +96,7 @@ index a77df7a2598..e1aa7526f9b 100644
return 2;
case FK_SecRel_4:
diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h
-index 02374966daf..01676a01683 100644
+index 0237496..01676a0 100644
--- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h
+++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h
@@ -36,6 +36,7 @@ public:
@@ -96,7 +108,7 @@ index 02374966daf..01676a01683 100644
bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup,
diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
-index 59f31be69d5..9b95598f99f 100644
+index 59f31be..9b95598 100644
--- a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
+++ b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
@@ -103,6 +103,9 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
@@ -109,8 +121,102 @@ index 59f31be69d5..9b95598f99f 100644
case ARM::fixup_arm_blx:
case ARM::fixup_arm_uncondbl:
switch (Modifier) {
+diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
+index 93f4006..67ae439 100644
+--- a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
++++ b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
+@@ -396,6 +396,7 @@ private:
+ void emitPad(int64_t Offset) override;
+ void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
+ bool isVector) override;
++ void emitLsda(const MCSymbol *Symbol) override;
+ void emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) override;
+
+@@ -461,6 +462,7 @@ public:
+ void emitMovSP(unsigned Reg, int64_t Offset = 0);
+ void emitPad(int64_t Offset);
+ void emitRegSave(const SmallVectorImpl<unsigned> &RegList, bool isVector);
++ void emitLsda(const MCSymbol *Symbol);
+ void emitUnwindRaw(int64_t Offset, const SmallVectorImpl<uint8_t> &Opcodes);
+
+ void ChangeSection(MCSection *Section, const MCExpr *Subsection) override {
+@@ -698,6 +700,7 @@ private:
+ bool CantUnwind;
+ SmallVector<uint8_t, 64> Opcodes;
+ UnwindOpcodeAssembler UnwindOpAsm;
++ const MCSymbol *Lsda;
+ };
+
+ } // end anonymous namespace
+@@ -740,6 +743,10 @@ void ARMTargetELFStreamer::emitRegSave(const SmallVectorImpl<unsigned> &RegList,
+ getStreamer().emitRegSave(RegList, isVector);
+ }
+
++void ARMTargetELFStreamer::emitLsda(const MCSymbol *Symbol) {
++ getStreamer().emitLsda(Symbol);
++}
++
+ void ARMTargetELFStreamer::emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ getStreamer().emitUnwindRaw(Offset, Opcodes);
+@@ -1233,6 +1240,7 @@ void ARMELFStreamer::EHReset() {
+ PendingOffset = 0;
+ UsedFP = false;
+ CantUnwind = false;
++ Lsda = nullptr;
+
+ Opcodes.clear();
+ UnwindOpAsm.Reset();
+@@ -1330,6 +1338,8 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool NoHandlerData) {
+ }
+
+ // Finalize the unwind opcode sequence
++ if (Lsda != nullptr && Opcodes.size() <= 4u)
++ PersonalityIndex = ARM::EHABI::AEABI_UNWIND_CPP_PR1;
+ UnwindOpAsm.Finalize(PersonalityIndex, Opcodes);
+
+ // For compact model 0, we have to emit the unwind opcodes in the .ARM.exidx
+@@ -1374,7 +1384,13 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool NoHandlerData) {
+ //
+ // In case that the .handlerdata directive is not specified by the
+ // programmer, we should emit zero to terminate the handler data.
+- if (NoHandlerData && !Personality)
++ if (Lsda != nullptr) {
++ const MCSymbolRefExpr *LsdaRef =
++ MCSymbolRefExpr::create(Lsda,
++ MCSymbolRefExpr::VK_None,
++ getContext());
++ EmitValue(LsdaRef, 4);
++ } else if (NoHandlerData && !Personality)
+ EmitIntValue(0, 4);
+ }
+
+@@ -1457,6 +1473,10 @@ void ARMELFStreamer::emitRegSave(const SmallVectorImpl<unsigned> &RegList,
+ UnwindOpAsm.EmitRegSave(Mask);
+ }
+
++void ARMELFStreamer::emitLsda(const MCSymbol *Symbol) {
++ Lsda = Symbol;
++}
++
+ void ARMELFStreamer::emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ FlushPendingOffset();
+diff --git a/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
+index 4a94318..f4f5aa1 100644
+--- a/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
++++ b/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
+@@ -61,6 +61,7 @@ void ARMTargetStreamer::emitMovSP(unsigned Reg, int64_t Offset) {}
+ void ARMTargetStreamer::emitPad(int64_t Offset) {}
+ void ARMTargetStreamer::emitRegSave(const SmallVectorImpl<unsigned> &RegList,
+ bool isVector) {}
++void ARMTargetStreamer::emitLsda(const MCSymbol *Symbol) {}
+ void ARMTargetStreamer::emitUnwindRaw(int64_t StackOffset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ }
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
-index b654b8c5cb8..58d25159af8 100644
+index b654b8c..58d2515 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -46,6 +46,7 @@ add_llvm_external_project(clang)
diff --git a/src/Native/ObjWriter/llvmCap/CMakeLists.txt b/src/Native/ObjWriter/llvmCap/CMakeLists.txt
index fd2e66ea1..a749ea878 100644
--- a/src/Native/ObjWriter/llvmCap/CMakeLists.txt
+++ b/src/Native/ObjWriter/llvmCap/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.6)
project(ObjWriter)
include(ExternalProject)
+set(LLVM_VERSION "5")
set(OBJWRITER_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../)
set(OBJWRITER_LLVM_POINT tools/ObjWriter)
@@ -36,12 +37,11 @@ if(USE_ARM_TARGET_TRIPLE)
list(APPEND LLVM_CMAKE_EXTRA_ARGS "-DLLVM_DEFAULT_TARGET_TRIPLE=thumbv7-linux-gnueabi")
endif()
-list(REMOVE_DUPLICATES CORERT_NATIVE_COMPILE_OPTIONS)
-
# Make sure to remove debug flags from general build flags for LLVM
set(LLVM_COMPILE_OPTIONS "${CORERT_NATIVE_COMPILE_OPTIONS}")
list(REMOVE_ITEM LLVM_COMPILE_OPTIONS "-g")
list(REMOVE_ITEM LLVM_COMPILE_OPTIONS "-O0")
+list(REMOVE_ITEM LLVM_COMPILE_OPTIONS "-Werror")
string(REPLACE ";" "\ " CORERT_NATIVE_COMPILE_OPTIONS "${CORERT_NATIVE_COMPILE_OPTIONS}")
string(REPLACE ";" "\ " LLVM_COMPILE_OPTIONS "${LLVM_COMPILE_OPTIONS}")
@@ -49,19 +49,32 @@ string(REPLACE ";" "\ " LLVM_COMPILE_OPTIONS "${LLVM_COMPILE_OPTIONS}")
# If host and target are the same, we could use llvm-tblgen from LLVM itself.
# Otherwise we use host llvm-tblgen. It's universal way for cross-building.
if(NOT ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ${CMAKE_SYSTEM_PROCESSOR})
+ # Find llvm-tblgen tool
+ if(NOT LLVM_TBLGEN_TOOL)
+ execute_process (
+ COMMAND bash -c "echo -n `which llvm-tblgen`"
+ OUTPUT_VARIABLE LLVM_TBLGEN_TOOL
+ )
+ if(NOT LLVM_TBLGEN_TOOL)
+ message(FATAL_ERROR "Can't find llvm-tblgen! You need to make sure that you have installed LLVM version $LLVM_VERSION")
+ endif()
+ endif()
+
+ # Check compatibility
execute_process (
- COMMAND bash -c "echo -n `which llvm-tblgen`"
- OUTPUT_VARIABLE LLVM_TBLGEN_TOOL
+ COMMAND bash -c "echo -n `${LLVM_TBLGEN_TOOL} --version | grep -i 'version ${LLVM_VERSION}'`"
+ OUTPUT_VARIABLE IS_LLVM_TBLGEN_TOOL_COMPATIBLE
)
- if(NOT LLVM_TBLGEN_TOOL)
- message(FATAL_ERROR "Can't find llvm-tblgen. You need to make sure that you have installed LLVM")
+ if(NOT IS_LLVM_TBLGEN_TOOL_COMPATIBLE)
+ message(FATAL_ERROR "LLVM version incompatibility! You need to make sure that you have installed LLVM version ${LLVM_VERSION}")
endif()
+
list(APPEND LLVM_CMAKE_EXTRA_ARGS "-DLLVM_TABLEGEN=${LLVM_TBLGEN_TOOL}")
endif()
ExternalProject_Add(LLVM
GIT_REPOSITORY https://github.com/llvm-mirror/llvm
- GIT_TAG release_50
+ GIT_TAG release_${LLVM_VERSION}0
GIT_SHALLOW 1
GIT_PROGRESS 1
PATCH_COMMAND ""
diff --git a/src/Native/ObjWriter/objwriter.cpp b/src/Native/ObjWriter/objwriter.cpp
index 7f81e070c..55f79aa47 100644
--- a/src/Native/ObjWriter/objwriter.cpp
+++ b/src/Native/ObjWriter/objwriter.cpp
@@ -32,6 +32,7 @@
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/MCELFStreamer.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
@@ -373,6 +374,11 @@ int ObjectWriter::EmitSymbolRef(const char *SymbolName,
Size = 4;
IsPCRel = true;
break;
+ case RelocType::IMAGE_REL_BASED_RELPTR32:
+ Size = 4;
+ IsPCRel = true;
+ Delta += 4; // size of C# (int) type is always 4 bytes
+ break;
case RelocType::IMAGE_REL_BASED_THUMB_MOV32: {
const unsigned Offset = GetDFSize();
const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta);
@@ -837,3 +843,56 @@ ObjectWriter::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberId
return TypeBuilder.GetMemberFunctionId(MemberIdDescriptor);
}
+void
+ObjectWriter::EmitARMFnStart() {
+ MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
+ ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
+
+ ATS.emitFnStart();
+}
+
+void ObjectWriter::EmitARMFnEnd() {
+ MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
+ ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
+
+ ATS.emitFnEnd();
+}
+
+void ObjectWriter::EmitARMExIdxLsda(const char *LsdaBlobSymbolName)
+{
+ MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
+ ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
+
+ MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName);
+ Assembler->registerSymbol(*T);
+
+ ATS.emitLsda(T);
+}
+
+void ObjectWriter::EmitARMExIdxCode(int Offset, const char *Blob)
+{
+ MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
+ ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
+ SmallVector<unsigned, 4> RegList;
+
+ const CFI_CODE *CfiCode = (const CFI_CODE *)Blob;
+ switch (CfiCode->CfiOpCode) {
+ case CFI_ADJUST_CFA_OFFSET:
+ assert(CfiCode->DwarfReg == DWARF_REG_ILLEGAL &&
+ "Unexpected Register Value for OpAdjustCfaOffset");
+ ATS.emitPad(CfiCode->Offset);
+ break;
+ case CFI_REL_OFFSET:
+ RegList.push_back(CfiCode->DwarfReg);
+ ATS.emitRegSave(RegList, false);
+ break;
+ case CFI_DEF_CFA_REGISTER:
+ assert(CfiCode->Offset == 0 &&
+ "Unexpected Offset Value for OpDefCfaRegister");
+ ATS.emitMovSP(CfiCode->DwarfReg, 0);
+ break;
+ default:
+ assert(false && "Unrecognized CFI");
+ break;
+ }
+}
diff --git a/src/Native/ObjWriter/objwriter.exports b/src/Native/ObjWriter/objwriter.exports
index c42c465df..460b22738 100644
--- a/src/Native/ObjWriter/objwriter.exports
+++ b/src/Native/ObjWriter/objwriter.exports
@@ -23,4 +23,8 @@ GetCompleteClassTypeIndex
GetArrayTypeIndex
GetPointerTypeIndex
GetMemberFunctionTypeIndex
-GetMemberFunctionIdTypeIndex \ No newline at end of file
+GetMemberFunctionIdTypeIndex
+EmitARMFnStart
+EmitARMFnEnd
+EmitARMExIdxCode
+EmitARMExIdxLsda \ No newline at end of file
diff --git a/src/Native/ObjWriter/objwriter.h b/src/Native/ObjWriter/objwriter.h
index 46b6bbd9f..21327b3be 100644
--- a/src/Native/ObjWriter/objwriter.h
+++ b/src/Native/ObjWriter/objwriter.h
@@ -24,6 +24,12 @@
using namespace llvm;
using namespace llvm::codeview;
+#ifdef _WIN32
+#define DLL_EXPORT extern "C" __declspec(dllexport)
+#else
+#define DLL_EXPORT extern "C" __attribute((visibility("default")))
+#endif
+
enum CustomSectionAttributes : int32_t {
CustomSectionAttributes_ReadOnly = 0x0000,
CustomSectionAttributes_Writeable = 0x0001,
@@ -38,6 +44,7 @@ enum class RelocType {
IMAGE_REL_BASED_DIR64 = 0x0A,
IMAGE_REL_BASED_REL32 = 0x10,
IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13,
+ IMAGE_REL_BASED_RELPTR32 = 0x7C,
};
class ObjectWriter {
@@ -91,6 +98,11 @@ public:
unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor);
+ void EmitARMFnStart();
+ void EmitARMFnEnd();
+ void EmitARMExIdxCode(int Offset, const char *Blob);
+ void EmitARMExIdxLsda(const char *Blob);
+
private:
void EmitLabelDiff(const MCSymbol *From, const MCSymbol *To,
unsigned int Size = 4);
@@ -155,7 +167,7 @@ private:
// When object writer is created/initialized successfully, it is returned.
// Or null object is returned. Client should check this.
-extern "C" ObjectWriter *InitObjWriter(const char *ObjectFilePath) {
+DLL_EXPORT ObjectWriter *InitObjWriter(const char *ObjectFilePath) {
ObjectWriter *OW = new ObjectWriter();
if (OW->Init(ObjectFilePath)) {
return OW;
@@ -164,20 +176,20 @@ extern "C" ObjectWriter *InitObjWriter(const char *ObjectFilePath) {
return nullptr;
}
-extern "C" void FinishObjWriter(ObjectWriter *OW) {
+DLL_EXPORT void FinishObjWriter(ObjectWriter *OW) {
assert(OW && "ObjWriter is null");
OW->Finish();
delete OW;
}
-extern "C" void SwitchSection(ObjectWriter *OW, const char *SectionName,
+DLL_EXPORT void SwitchSection(ObjectWriter *OW, const char *SectionName,
CustomSectionAttributes attributes,
const char *ComdatName) {
assert(OW && "ObjWriter is null");
OW->SwitchSection(SectionName, attributes, ComdatName);
}
-extern "C" void SetCodeSectionAttribute(ObjectWriter *OW,
+DLL_EXPORT void SetCodeSectionAttribute(ObjectWriter *OW,
const char *SectionName,
CustomSectionAttributes attributes,
const char *ComdatName) {
@@ -185,80 +197,80 @@ extern "C" void SetCodeSectionAttribute(ObjectWriter *OW,
OW->SetCodeSectionAttribute(SectionName, attributes, ComdatName);
}
-extern "C" void EmitAlignment(ObjectWriter *OW, int ByteAlignment) {
+DLL_EXPORT void EmitAlignment(ObjectWriter *OW, int ByteAlignment) {
assert(OW && "ObjWriter is null");
OW->EmitAlignment(ByteAlignment);
}
-extern "C" void EmitBlob(ObjectWriter *OW, int BlobSize, const char *Blob) {
+DLL_EXPORT void EmitBlob(ObjectWriter *OW, int BlobSize, const char *Blob) {
assert(OW && "ObjWriter null");
OW->EmitBlob(BlobSize, Blob);
}
-extern "C" void EmitIntValue(ObjectWriter *OW, uint64_t Value, unsigned Size) {
+DLL_EXPORT void EmitIntValue(ObjectWriter *OW, uint64_t Value, unsigned Size) {
assert(OW && "ObjWriter is null");
OW->EmitIntValue(Value, Size);
}
-extern "C" void EmitSymbolDef(ObjectWriter *OW, const char *SymbolName) {
+DLL_EXPORT void EmitSymbolDef(ObjectWriter *OW, const char *SymbolName) {
assert(OW && "ObjWriter is null");
OW->EmitSymbolDef(SymbolName);
}
-extern "C" int EmitSymbolRef(ObjectWriter *OW, const char *SymbolName,
+DLL_EXPORT int EmitSymbolRef(ObjectWriter *OW, const char *SymbolName,
RelocType RelocType, int Delta) {
assert(OW && "ObjWriter is null");
return OW->EmitSymbolRef(SymbolName, RelocType, Delta);
}
-extern "C" void EmitWinFrameInfo(ObjectWriter *OW, const char *FunctionName,
+DLL_EXPORT void EmitWinFrameInfo(ObjectWriter *OW, const char *FunctionName,
int StartOffset, int EndOffset,
const char *BlobSymbolName) {
assert(OW && "ObjWriter is null");
OW->EmitWinFrameInfo(FunctionName, StartOffset, EndOffset, BlobSymbolName);
}
-extern "C" void EmitCFIStart(ObjectWriter *OW, int Offset) {
+DLL_EXPORT void EmitCFIStart(ObjectWriter *OW, int Offset) {
assert(OW && "ObjWriter is null");
OW->EmitCFIStart(Offset);
}
-extern "C" void EmitCFIEnd(ObjectWriter *OW, int Offset) {
+DLL_EXPORT void EmitCFIEnd(ObjectWriter *OW, int Offset) {
assert(OW && "ObjWriter is null");
OW->EmitCFIEnd(Offset);
}
-extern "C" void EmitCFILsda(ObjectWriter *OW, const char *LsdaBlobSymbolName) {
+DLL_EXPORT void EmitCFILsda(ObjectWriter *OW, const char *LsdaBlobSymbolName) {
assert(OW && "ObjWriter is null");
OW->EmitCFILsda(LsdaBlobSymbolName);
}
-extern "C" void EmitCFICode(ObjectWriter *OW, int Offset, const char *Blob) {
+DLL_EXPORT void EmitCFICode(ObjectWriter *OW, int Offset, const char *Blob) {
assert(OW && "ObjWriter is null");
OW->EmitCFICode(Offset, Blob);
}
-extern "C" void EmitDebugFileInfo(ObjectWriter *OW, int FileId,
+DLL_EXPORT void EmitDebugFileInfo(ObjectWriter *OW, int FileId,
const char *FileName) {
assert(OW && "ObjWriter is null");
OW->EmitDebugFileInfo(FileId, FileName);
}
-extern "C" void EmitDebugFunctionInfo(ObjectWriter *OW,
+DLL_EXPORT void EmitDebugFunctionInfo(ObjectWriter *OW,
const char *FunctionName,
int FunctionSize) {
assert(OW && "ObjWriter is null");
OW->EmitDebugFunctionInfo(FunctionName, FunctionSize);
}
-extern "C" void EmitDebugVar(ObjectWriter *OW, char *Name, int TypeIndex,
+DLL_EXPORT void EmitDebugVar(ObjectWriter *OW, char *Name, int TypeIndex,
bool IsParam, int RangeCount,
ICorDebugInfo::NativeVarInfo *Ranges) {
assert(OW && "ObjWriter is null");
OW->EmitDebugVar(Name, TypeIndex, IsParam, RangeCount, Ranges);
}
-extern "C" void EmitDebugLoc(ObjectWriter *OW, int NativeOffset, int FileId,
+DLL_EXPORT void EmitDebugLoc(ObjectWriter *OW, int NativeOffset, int FileId,
int LineNumber, int ColNumber) {
assert(OW && "ObjWriter is null");
OW->EmitDebugLoc(NativeOffset, FileId, LineNumber, ColNumber);
@@ -266,25 +278,25 @@ extern "C" void EmitDebugLoc(ObjectWriter *OW, int NativeOffset, int FileId,
// This should be invoked at the end of module emission to finalize
// debug module info.
-extern "C" void EmitDebugModuleInfo(ObjectWriter *OW) {
+DLL_EXPORT void EmitDebugModuleInfo(ObjectWriter *OW) {
assert(OW && "ObjWriter is null");
OW->EmitDebugModuleInfo();
}
-extern "C" unsigned GetEnumTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetEnumTypeIndex(ObjectWriter *OW,
EnumTypeDescriptor TypeDescriptor,
EnumRecordTypeDescriptor *TypeRecords) {
assert(OW && "ObjWriter is null");
return OW->GetEnumTypeIndex(TypeDescriptor, TypeRecords);
}
-extern "C" unsigned GetClassTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetClassTypeIndex(ObjectWriter *OW,
ClassTypeDescriptor ClassDescriptor) {
assert(OW && "ObjWriter is null");
return OW->GetClassTypeIndex(ClassDescriptor);
}
-extern "C" unsigned
+DLL_EXPORT unsigned
GetCompleteClassTypeIndex(ObjectWriter *OW, ClassTypeDescriptor ClassDescriptor,
ClassFieldsTypeDescriptior ClassFieldsDescriptor,
DataFieldDescriptor *FieldsDescriptors) {
@@ -293,28 +305,48 @@ GetCompleteClassTypeIndex(ObjectWriter *OW, ClassTypeDescriptor ClassDescriptor,
FieldsDescriptors);
}
-extern "C" unsigned GetArrayTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetArrayTypeIndex(ObjectWriter *OW,
ClassTypeDescriptor ClassDescriptor,
ArrayTypeDescriptor ArrayDescriptor) {
assert(OW && "ObjWriter is null");
return OW->GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor);
}
-extern "C" unsigned GetPointerTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetPointerTypeIndex(ObjectWriter *OW,
PointerTypeDescriptor PointerDescriptor) {
assert(OW && "ObjWriter is null");
return OW->GetPointerTypeIndex(PointerDescriptor);
}
-extern "C" unsigned GetMemberFunctionTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetMemberFunctionTypeIndex(ObjectWriter *OW,
MemberFunctionTypeDescriptor MemberDescriptor,
uint32_t *ArgumentTypes) {
assert(OW && "ObjWriter is null");
return OW->GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes);
}
-extern "C" unsigned GetMemberFunctionIdTypeIndex(ObjectWriter *OW,
+DLL_EXPORT unsigned GetMemberFunctionIdTypeIndex(ObjectWriter *OW,
MemberFunctionIdTypeDescriptor MemberIdDescriptor) {
assert(OW && "ObjWriter is null");
return OW->GetMemberFunctionId(MemberIdDescriptor);
}
+
+DLL_EXPORT void EmitARMFnStart(ObjectWriter *OW) {
+ assert(OW && "ObjWriter is null");
+ return OW->EmitARMFnStart();
+}
+
+DLL_EXPORT void EmitARMFnEnd(ObjectWriter *OW) {
+ assert(OW && "ObjWriter is null");
+ return OW->EmitARMFnEnd();
+}
+
+DLL_EXPORT void EmitARMExIdxLsda(ObjectWriter *OW, const char *Blob) {
+ assert(OW && "ObjWriter is null");
+ return OW->EmitARMExIdxLsda(Blob);
+}
+
+DLL_EXPORT void EmitARMExIdxCode(ObjectWriter *OW, int Offset, const char *Blob) {
+ assert(OW && "ObjWriter is null");
+ return OW->EmitARMExIdxCode(Offset, Blob);
+}
diff --git a/src/Native/Runtime/AsmOffsets.h b/src/Native/Runtime/AsmOffsets.h
index 299454a28..34b8e5fd8 100644
--- a/src/Native/Runtime/AsmOffsets.h
+++ b/src/Native/Runtime/AsmOffsets.h
@@ -50,8 +50,11 @@ ASM_OFFSET( 2c, 40, Thread, m_pTransitionFrame)
ASM_OFFSET( 30, 48, Thread, m_pHackPInvokeTunnel)
ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation)
ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress)
-ASM_OFFSET( 48, 78, Thread, m_pExInfoStackHead)
-ASM_OFFSET( 4c, 80, Thread, m_threadAbortException)
+#ifdef BIT64
+ASM_OFFSET( 0, 78, Thread, m_uHijackedReturnValueFlags)
+#endif
+ASM_OFFSET( 48, 80, Thread, m_pExInfoStackHead)
+ASM_OFFSET( 4c, 88, Thread, m_threadAbortException)
ASM_SIZEOF( 14, 20, EHEnum)
diff --git a/src/Native/Runtime/AsmOffsetsVerify.cpp b/src/Native/Runtime/AsmOffsetsVerify.cpp
index 2e7343ce1..13793b7e6 100644
--- a/src/Native/Runtime/AsmOffsetsVerify.cpp
+++ b/src/Native/Runtime/AsmOffsetsVerify.cpp
@@ -44,3 +44,7 @@ class AsmOffsets
#include "AsmOffsets.h"
};
+
+#ifdef _MSC_VER
+namespace { char WorkaroundLNK4221Warning; };
+#endif
diff --git a/src/Native/Runtime/CachedInterfaceDispatch.cpp b/src/Native/Runtime/CachedInterfaceDispatch.cpp
index c49d13129..63a506baf 100644
--- a/src/Native/Runtime/CachedInterfaceDispatch.cpp
+++ b/src/Native/Runtime/CachedInterfaceDispatch.cpp
@@ -510,7 +510,7 @@ COOP_PINVOKE_HELPER(PTR_Code, RhpUpdateDispatchCellCache, (InterfaceDispatchCell
// Publish the new cache by atomically updating both the cache and stub pointers in the indirection
// cell. This returns us a cache to discard which may be NULL (no previous cache), the previous cache
- // value or the cache we just allocated (another thread peformed an update first).
+ // value or the cache we just allocated (another thread performed an update first).
InterfaceDispatchCache * pDiscardedCache = UpdateCellStubAndCache(pCell, pStub, newCacheValue);
if (pDiscardedCache)
DiscardCache(pDiscardedCache);
diff --git a/src/Native/Runtime/DebugFuncEval.cpp b/src/Native/Runtime/DebugFuncEval.cpp
index ae920a46a..e5362eaa4 100644
--- a/src/Native/Runtime/DebugFuncEval.cpp
+++ b/src/Native/Runtime/DebugFuncEval.cpp
@@ -5,11 +5,14 @@
#include "common.h"
#include "CommonTypes.h"
#include "DebugFuncEval.h"
+#include "rhassert.h"
+#include "RWLock.h"
+#include "slist.h"
+#include "RuntimeInstance.h"
GVAL_IMPL_INIT(UInt32, g_FuncEvalMode, 0);
GVAL_IMPL_INIT(UInt32, g_FuncEvalParameterBufferSize, 0);
GVAL_IMPL_INIT(UInt64, g_MostRecentFuncEvalHijackInstructionPointer, 0);
-GPTR_IMPL_INIT(PTR_VOID, g_HighLevelDebugFuncEvalAbortHelperAddr, 0);
#ifndef DACCESS_COMPILE
@@ -28,16 +31,6 @@ GPTR_IMPL_INIT(PTR_VOID, g_HighLevelDebugFuncEvalAbortHelperAddr, 0);
return g_MostRecentFuncEvalHijackInstructionPointer;
}
-/* static */ HighLevelDebugFuncEvalAbortHelperType DebugFuncEval::GetHighLevelDebugFuncEvalAbortHelper()
-{
- return (HighLevelDebugFuncEvalAbortHelperType)g_HighLevelDebugFuncEvalAbortHelperAddr;
-}
-
-/* static */ void DebugFuncEval::SetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper)
-{
- g_HighLevelDebugFuncEvalAbortHelperAddr = (PTR_PTR_VOID)highLevelDebugFuncEvalAbortHelper;
-}
-
/// <summary>
/// Retrieve the global FuncEval parameter buffer size.
/// </summary>
@@ -73,22 +66,12 @@ EXTERN_C REDHAWK_API UInt32 __cdecl RhpGetFuncEvalMode()
/// <remarks>
/// This is the entry point of FuncEval abort
/// When the debugger decides to abort the FuncEval, it will create a remote thread calling this function.
-/// This function will call back into the highLevelDebugFuncEvalAbortHelper to perform the abort.
+/// This function will call back into the DebugFuncEvalAbortHelper to perform the abort.
EXTERN_C REDHAWK_API void __cdecl RhpInitiateFuncEvalAbort(void* pointerFromDebugger)
{
- HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper = DebugFuncEval::GetHighLevelDebugFuncEvalAbortHelper();
- highLevelDebugFuncEvalAbortHelper((UInt64)pointerFromDebugger);
-}
-
-/// <summary>
-/// Set the high level debug func eval abort helper
-/// </summary>
-/// <remarks>
-/// The high level debug func eval abort helper is a function that perform the actual func eval abort
-/// It is implemented in System.Private.Debug.dll
-EXTERN_C REDHAWK_API void __cdecl RhpSetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper)
-{
- DebugFuncEval::SetHighLevelDebugFuncEvalAbortHelper(highLevelDebugFuncEvalAbortHelper);
+ DebugFuncEvalAbortHelperFunctionType debugFuncEvalAbortHelperFunction = (DebugFuncEvalAbortHelperFunctionType)GetRuntimeInstance()->GetClasslibFunctionFromCodeAddress((void*)g_MostRecentFuncEvalHijackInstructionPointer, ClasslibFunctionId::DebugFuncEvalAbortHelper);
+ ASSERT(debugFuncEvalAbortHelperFunction != nullptr);
+ debugFuncEvalAbortHelperFunction((Int64)pointerFromDebugger);
}
#else
@@ -103,4 +86,4 @@ UInt64 DebugFuncEval::GetMostRecentFuncEvalHijackInstructionPointer()
EXTERN_C void * RhpDebugFuncEvalHelper;
GPTR_IMPL_INIT(PTR_VOID, g_RhpDebugFuncEvalHelperAddr, &RhpDebugFuncEvalHelper);
-GPTR_IMPL_INIT(PTR_VOID, g_RhpInitiateFuncEvalAbortAddr, (void**)&RhpInitiateFuncEvalAbort); \ No newline at end of file
+GPTR_IMPL_INIT(PTR_VOID, g_RhpInitiateFuncEvalAbortAddr, (void**)&RhpInitiateFuncEvalAbort);
diff --git a/src/Native/Runtime/DebugFuncEval.h b/src/Native/Runtime/DebugFuncEval.h
index 20315c35a..dc9c25efc 100644
--- a/src/Native/Runtime/DebugFuncEval.h
+++ b/src/Native/Runtime/DebugFuncEval.h
@@ -15,7 +15,7 @@
#ifndef DACCESS_COMPILE
-typedef void(*HighLevelDebugFuncEvalAbortHelperType)(UInt64);
+typedef void(*DebugFuncEvalAbortHelperFunctionType)(UInt64);
class DebugFuncEval
{
@@ -51,18 +51,6 @@ public:
/// It is used for the stack walker to understand the hijack frame
/// </remarks>
static UInt64 GetMostRecentFuncEvalHijackInstructionPointer();
-
- /// <summary>
- /// Retrieve the high level debug func eval abort helper
- /// </summary>
- static HighLevelDebugFuncEvalAbortHelperType GetHighLevelDebugFuncEvalAbortHelper();
-
-
- /// <summary>
- /// Set the high level debug func eval abort helper
- /// </summary>
- static void SetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper);
-
};
#else
diff --git a/src/Native/Runtime/EHHelpers.cpp b/src/Native/Runtime/EHHelpers.cpp
index fd3c2f7a6..4a8adf62e 100644
--- a/src/Native/Runtime/EHHelpers.cpp
+++ b/src/Native/Runtime/EHHelpers.cpp
@@ -28,29 +28,6 @@
#include "rhbinder.h"
#include "eetype.h"
-// Find the code manager containing the given address, which might be a return address from a managed function. The
-// address may be to another managed function, or it may be to an unmanaged function. The address may also refer to
-// an EEType.
-static ICodeManager * FindCodeManagerForClasslibFunction(void * address)
-{
- RuntimeInstance * pRI = GetRuntimeInstance();
-
- // Try looking up the code manager assuming the address is for code first. This is expected to be most common.
- ICodeManager * pCodeManager = pRI->FindCodeManagerByAddress(address);
- if (pCodeManager != NULL)
- return pCodeManager;
-
- // Less common, we will look for the address in any of the sections of the module. This is slower, but is
- // necessary for EEType pointers and jump stubs.
- Module * pModule = pRI->FindModuleByAddress(address);
- if (pModule != NULL)
- return pModule;
-
- ASSERT_MSG(!Thread::IsHijackTarget(address), "not expected to be called with hijacked return address");
-
- return NULL;
-}
-
COOP_PINVOKE_HELPER(Boolean, RhpEHEnumInitFromStackFrameIterator, (
StackFrameIterator* pFrameIter, void ** pMethodStartAddressOut, EHEnum* pEHEnum))
{
@@ -70,18 +47,7 @@ COOP_PINVOKE_HELPER(Boolean, RhpEHEnumNext, (EHEnum* pEHEnum, EHClause* pEHClaus
// found via the provided address does not have the necessary exports.
COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromCodeAddress, (void * address, ClasslibFunctionId functionId))
{
- // Find the code manager for the given address, which is an address into some managed module. It could
- // be code, or it could be an EEType. No matter what, it's an address into a managed module in some non-Rtm
- // type system.
- ICodeManager * pCodeManager = FindCodeManagerForClasslibFunction(address);
-
- // If the address isn't in a managed module then we have no classlib function.
- if (pCodeManager == NULL)
- {
- return NULL;
- }
-
- return pCodeManager->GetClasslibFunction(functionId);
+ return GetRuntimeInstance()->GetClasslibFunctionFromCodeAddress(address, functionId);
}
// Unmanaged helper to locate one of two classlib-provided functions that the runtime needs to
@@ -139,8 +105,7 @@ COOP_PINVOKE_HELPER(Int32, RhGetModuleFileName, (HANDLE moduleHandle, _Out_ cons
return PalGetModuleFileName(pModuleNameOut, moduleHandle);
}
-COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo,
- (void * pOSContext, Int32 cbOSContext, PAL_LIMITED_CONTEXT * pPalContext))
+COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo, (void * pOSContext, Int32 cbOSContext, PAL_LIMITED_CONTEXT * pPalContext))
{
UNREFERENCED_PARAMETER(cbOSContext);
ASSERT(cbOSContext >= sizeof(CONTEXT));
@@ -214,9 +179,7 @@ COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo,
#endif
}
-
#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_) || defined(_ARM64_)
-// ARM64TODO
struct DISPATCHER_CONTEXT
{
UIntNative ControlPc;
@@ -238,9 +201,9 @@ EXTERN_C void REDHAWK_CALLCONV RhpFailFastForPInvokeExceptionCoop(IntNative PInv
Int32 __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs);
EXTERN_C Int32 __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExceptionRecord,
- UIntNative EstablisherFrame,
- PCONTEXT pContextRecord,
- DISPATCHER_CONTEXT * pDispatcherContext)
+ UIntNative EstablisherFrame,
+ PCONTEXT pContextRecord,
+ DISPATCHER_CONTEXT * pDispatcherContext)
{
UNREFERENCED_PARAMETER(EstablisherFrame);
#ifdef APP_LOCAL_RUNTIME
@@ -268,7 +231,6 @@ EXTERN_C Int32 __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExcep
if (pThread->IsDoNotTriggerGcSet())
RhFailFast();
-
// We promote exceptions that were not converted to managed exceptions to a FailFast. However, we have to
// be careful because we got here via OS SEH infrastructure and, therefore, don't know what GC mode we're
// currently in. As a result, since we're calling back into managed code to handle the FailFast, we must
@@ -423,7 +385,8 @@ static UIntNative UnwindWriteBarrierToCaller(
#ifdef PLATFORM_UNIX
-Int32 __stdcall RhpHardwareExceptionHandler(UIntNative faultCode, UIntNative faultAddress, PAL_LIMITED_CONTEXT* palContext, UIntNative* arg0Reg, UIntNative* arg1Reg)
+Int32 __stdcall RhpHardwareExceptionHandler(UIntNative faultCode, UIntNative faultAddress,
+ PAL_LIMITED_CONTEXT* palContext, UIntNative* arg0Reg, UIntNative* arg1Reg)
{
UIntNative faultingIP = palContext->GetIp();
@@ -545,5 +508,4 @@ COOP_PINVOKE_HELPER(void, RhpFallbackFailFast, ())
RhFailFast();
}
-
#endif // !DACCESS_COMPILE
diff --git a/src/Native/Runtime/ICodeManager.h b/src/Native/Runtime/ICodeManager.h
index efd5054d2..fe4eb6ce9 100644
--- a/src/Native/Runtime/ICodeManager.h
+++ b/src/Native/Runtime/ICodeManager.h
@@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.
#pragma once
+#define ICODEMANAGER_INCLUDED
+
// TODO: Debugger/DAC support (look for TODO: JIT)
struct REGDISPLAY;
@@ -23,14 +25,43 @@ struct GCEnumContext
GCEnumCallback pCallback;
};
+// All values but GCRK_Unknown must correspond to MethodReturnKind enumeration in gcinfo.h
enum GCRefKind : unsigned char
-{
- GCRK_Scalar = 0x00,
- GCRK_Object = 0x01,
- GCRK_Byref = 0x02,
- GCRK_Unknown = 0xFF,
+{
+ GCRK_Scalar = 0x00,
+ GCRK_Object = 0x01,
+ GCRK_Byref = 0x02,
+#ifdef _TARGET_ARM64_
+ // Composite return kinds for value types returned in two registers (encoded with two bits per register)
+ GCRK_Scalar_Obj = (GCRK_Object << 2) | GCRK_Scalar,
+ GCRK_Obj_Obj = (GCRK_Object << 2) | GCRK_Object,
+ GCRK_Byref_Obj = (GCRK_Object << 2) | GCRK_Byref,
+ GCRK_Scalar_Byref = (GCRK_Byref << 2) | GCRK_Scalar,
+ GCRK_Obj_Byref = (GCRK_Byref << 2) | GCRK_Object,
+ GCRK_Byref_Byref = (GCRK_Byref << 2) | GCRK_Byref,
+
+ GCRK_LastValid = GCRK_Byref_Byref,
+#else // _TARGET_ARM64_
+ GCRK_LastValid = GCRK_Byref,
+#endif // _TARGET_ARM64_
+ GCRK_Unknown = 0xFF,
};
+#ifdef _TARGET_ARM64_
+// Extract individual GCRefKind components from a composite return kind
+inline GCRefKind ExtractReg0ReturnKind(GCRefKind returnKind)
+{
+ ASSERT(returnKind <= GCRK_LastValid);
+ return (GCRefKind)(returnKind & (GCRK_Object | GCRK_Byref));
+}
+
+inline GCRefKind ExtractReg1ReturnKind(GCRefKind returnKind)
+{
+ ASSERT(returnKind <= GCRK_LastValid);
+ return (GCRefKind)(returnKind >> 2);
+}
+#endif // _TARGET_ARM64_
+
//
// MethodInfo is placeholder type used to allocate space for MethodInfo. Maximum size
// of the actual method should be less or equal to the placeholder size.
@@ -66,9 +97,7 @@ struct EHClause
void* m_pTargetType;
};
-// Constants used with RhpGetClasslibFunction, to indicate which classlib function
-// we are interested in.
-// Note: make sure you change the def in System\Runtime\exceptionhandling.cs if you change this!
+// Note: make sure you change the def in System\Runtime\InternalCalls.cs if you change this!
enum class ClasslibFunctionId
{
GetRuntimeException = 0,
@@ -78,6 +107,8 @@ enum class ClasslibFunctionId
CheckStaticClassConstruction = 4,
GetSystemArrayEEType = 5,
OnFirstChanceException = 6,
+ DebugFuncEvalHelper = 7,
+ DebugFuncEvalAbortHelper = 8,
};
enum class AssociatedDataFlags : unsigned char
diff --git a/src/Native/Runtime/MiscHelpers.cpp b/src/Native/Runtime/MiscHelpers.cpp
index 05811e91d..c74571587 100644
--- a/src/Native/Runtime/MiscHelpers.cpp
+++ b/src/Native/Runtime/MiscHelpers.cpp
@@ -553,9 +553,19 @@ COOP_PINVOKE_HELPER(UInt8 *, RhGetCodeTarget, (UInt8 * pCodeOrg))
pCode++;
}
// is this an indirect jump?
- if (/* ARM64TODO */ false)
+ // adrp xip0,#imm21; ldr xip0,[xip0,#imm12]; br xip0
+ if ((pCode[0] & 0x9f00001f) == 0x90000010 &&
+ (pCode[1] & 0xffc003ff) == 0xf9400210 &&
+ pCode[2] == 0xd61f0200)
{
- // ARM64TODO
+ // normal import stub - dist to IAT cell is relative to (PC & ~0xfff)
+ // adrp: imm = SignExtend(immhi:immlo:Zeros(12), 64);
+ Int64 distToIatCell = (((((Int64)pCode[0] & ~0x1f) << 40) >> 31) | ((pCode[0] >> 17) & 0x3000));
+ // ldr: offset = LSL(ZeroExtend(imm12, 64), 3);
+ distToIatCell += (pCode[1] >> 7) & 0x7ff8;
+ UInt8 ** pIatCell = (UInt8 **)(((Int64)pCode & ~0xfff) + distToIatCell);
+ ASSERT(pModule == NULL || pModule->ContainsDataAddress(pIatCell));
+ return *pIatCell;
}
// is this an unboxing stub followed by a relative jump?
else if (unboxingStub && (pCode[0] >> 26) == 0x5)
@@ -697,7 +707,7 @@ COOP_PINVOKE_HELPER(Boolean, RhpArrayCopy, (Array * pSourceArray, Int32 sourceIn
//
// This function handles all cases of Array.Clear that do not require conversions. It returns false if the operation cannot be performed, leaving
-// the handling of the complex cases or throwing apppropriate exception to the higher level framework. It is only allowed to return false for illegal
+// the handling of the complex cases or throwing appropriate exception to the higher level framework. It is only allowed to return false for illegal
// calls as the BCL side has fallback for "complex cases" only.
//
COOP_PINVOKE_HELPER(Boolean, RhpArrayClear, (Array * pArray, Int32 index, Int32 length))
diff --git a/src/Native/Runtime/Portable/CMakeLists.txt b/src/Native/Runtime/Portable/CMakeLists.txt
index f191db46d..21dbb7d7c 100644
--- a/src/Native/Runtime/Portable/CMakeLists.txt
+++ b/src/Native/Runtime/Portable/CMakeLists.txt
@@ -26,9 +26,9 @@ else()
endif()
add_custom_command(
- # The AsmOffsets.cs is consumed later by the managed build
+ # The AsmOffsetsPortable.cs is consumed later by the managed build
TARGET PortableRuntime
- COMMAND ${CMAKE_CXX_COMPILER} ${COMPILER_LANGUAGE} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CSPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.cs"
+ COMMAND ${CMAKE_CXX_COMPILER} ${COMPILER_LANGUAGE} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CSPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsetsPortable.cs"
DEPENDS "${RUNTIME_DIR}/AsmOffsets.cpp" "${RUNTIME_DIR}/AsmOffsets.h"
)
diff --git a/src/Native/Runtime/RHCodeMan.cpp b/src/Native/Runtime/RHCodeMan.cpp
index 9eefba4e0..b6a173bb2 100644
--- a/src/Native/Runtime/RHCodeMan.cpp
+++ b/src/Native/Runtime/RHCodeMan.cpp
@@ -97,7 +97,7 @@ void ReportRegisterSet(UInt8 regSet, REGDISPLAY * pContext, GCEnumContext * hCal
{
// 2. 00lRRRRR - normal "register set" encoding, pinned and interior attributes both false
// a. l - this is the last descriptor
- // b. RRRRR - this is the register mask for { r4, r5, r6, r7, r8 }
+ // b. RRRRR - this is the register mask for { r4-r8 }
if (regSet & CSR_MASK_R4) { ReportObject(hCallback, GetRegObjectAddr<CSR_NUM_R4>(pContext), 0); }
if (regSet & CSR_MASK_R5) { ReportObject(hCallback, GetRegObjectAddr<CSR_NUM_R5>(pContext), 0); }
@@ -373,9 +373,10 @@ void ReportLocalSlots(UInt8 localsEnc, REGDISPLAY * pContext, GCEnumContext * hC
{
// 4. 10l1SSSS - "local stack slot set" encoding, pinned and interior attributes both false
// a. l - last descriptor
- // b. SSSS - set of "local slots" #0 - #3 - local slot 0 is at offset -8 from the last pushed
- // callee saved register, local slot 1 is at offset - 16, etc - in other words, these are the
- // slots normally used for locals
+ // b. SSSS - set of "local slots" #0-#3 - local slot #0 is at offset -POINTER_SIZE from
+ // the last pushed callee saved register, local slot #1 is at offset -2*POINTER_SIZE,
+ // etc - in other words, these are the slots normally used for locals. The non-sensical
+ // encoding with SSSS = 0000 is reserved for the "common vars" case 8 below.
if (localsEnc & 0x01) { ReportLocalSlot(0, pContext, hCallback, pHeader); }
if (localsEnc & 0x02) { ReportLocalSlot(1, pContext, hCallback, pHeader); }
if (localsEnc & 0x04) { ReportLocalSlot(2, pContext, hCallback, pHeader); }
@@ -385,7 +386,7 @@ void ReportLocalSlots(UInt8 localsEnc, REGDISPLAY * pContext, GCEnumContext * hC
{
// 5. 10l0ssss - "local slot" encoding, pinned and interior attributes are both false
// a. l - last descriptor
- // b. ssss - "local slot" #4 - #19
+ // b. ssss - "local slot" #4-#19 (#0-#3 are encoded by case 4 above)
UInt32 localNum = (localsEnc & 0xF) + 4;
ReportLocalSlot(localNum, pContext, hCallback, pHeader);
}
@@ -401,10 +402,10 @@ void ReportStackSlots(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext *
// e. s - offset sign
// f. m - mask follows
// g. offset - variable length unsigned integer
- // h. mask - variable length unsigned integer (only present if m-bit is 1) - this can describe
- // multiple stack locations with the same attributes. E.g., if you want to describe stack
- // locations 0x20, 0x28, 0x38, you would give a (starting) offset of 0x20 and a mask of
- // 000000101 = 0x05. Up to 33 stack locations can be described.
+ // h. mask - variable length unsigned integer (only present if m-bit is 1) - describes multiple
+ // (up to 33) stack locations having the same attributes. E.g., to describe stack locations
+ // 0x20, 0x28, 0x38, you specify the starting offset 0x20 and the mask 000000101 = 0x5.
+ // The mask describes the next 32 stack locations after the first one.
UInt32 flags = 0;
if (firstEncByte & 0x08) { flags |= GC_CALL_PINNED; }
@@ -478,7 +479,7 @@ void ReportScratchRegs(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext
// e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1
// f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1
//
- // For ARM64 the scheme above is extended to support the bigger register set:
+ // For ARM64 the scheme above is extended to support the larger register set:
// - 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] for { x0-x6 }
// - 11lip010 1RRRRRRR 0RRRRRRR [[1IIIIIII] 0IIIIIII] [[1PPPPPPP] 0PPPPPPP] for { x0-x13 }
// - 11lip010 1RRRRRRR 1RRRRRRR 000RRRRR [0*2(1IIIIIII) 000IIIII] [0*2(1PPPPPPP) 000PPPPP] for { x0-x15, xip0, xip1, lr }
@@ -505,7 +506,7 @@ void ReportScratchRegs(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext
}
// Enumerate all live object references in that function using the virtual register set. Same reference
-// location cannot be enumerated multiple times (but all differenct references pointing to the same object
+// location cannot be enumerated multiple times (but all different references pointing to the same object
// have to be individually enumerated).
// Returns success of operation.
void EECodeManager::EnumGcRefs(MethodGcInfoPointers * pMethodInfo,
@@ -632,7 +633,7 @@ ContinueUnconditionally:
// a. l - this is the last descriptor
// b. RRRRR - this is the register mask for { rbx, rsi, rdi, rbp, r12 }, ARM = { r4-r8 }
//
- // For ARM64 the scheme above is extended to support the bigger register set:
+ // For ARM64 the scheme above is extended to support the larger register set:
// 00lvRRRR [RRRRRRRR] - normal "register set" encoding, pinned and interior attributes both false
// a. l - this is the last descriptor
// b. v - extra byte follows
@@ -648,14 +649,14 @@ ContinueUnconditionally:
//
// 4. 10l1SSSS - "local stack slot set" encoding, pinned and interior attributes both false
// a. l - last descriptor
- // b. SSSS - set of "local slots" #0 - #3 - local slot 0 is at offset -8 from the last pushed
- // callee saved register, local slot 1 is at offset - 16, etc - in other words, these are the
- // slots normally used for locals. The non-sensical encoding with SSSS = 0000 is reserved for
- // the "common vars" case under 8 below.
+ // b. SSSS - set of "local slots" #0-#3 - local slot #0 is at offset -POINTER_SIZE from
+ // the last pushed callee saved register, local slot #1 is at offset -2*POINTER_SIZE,
+ // etc - in other words, these are the slots normally used for locals. The non-sensical
+ // encoding with SSSS = 0000 is reserved for the "common vars" case 8 below.
//
- // 5. 10l0ssss - "local slot" encoding
+ // 5. 10l0ssss - "local slot" encoding, pinned and interior attributes are both false
// a. l - last descriptor
- // b. ssss - "local slot" #4 - #19
+ // b. ssss - "local slot" #4-#19 (#0-#3 are encoded by case 4 above)
//
// 6. 11lipfsm {offset} [mask] - [multiple] stack slot encoding
// a. l - last descriptor
@@ -665,10 +666,10 @@ ContinueUnconditionally:
// e. s - offset sign
// f. m - mask follows
// g. offset - variable length unsigned integer
- // h. mask - variable length unsigned integer (only present if m-bit is 1) - this can describe
- // multiple stack locations with the same attributes. E.g., if you want to describe stack
- // locations 0x20, 0x28, 0x38, you would give a (starting) offset of 0x20 and a mask of
- // 000000101 = 0x05. Up to 33 stack locations can be described.
+ // h. mask - variable length unsigned integer (only present if m-bit is 1) - describes multiple
+ // (up to 33) stack locations having the same attributes. E.g., to describe stack locations
+ // 0x20, 0x28, 0x38, you specify the starting offset 0x20 and the mask 000000101 = 0x5.
+ // The mask describes the next 32 stack locations after the first one.
//
// 7. 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] - live scratch reg reporting, this uses the SP-xxx encoding
// from #6 since we cannot have stack locations at negative
@@ -680,7 +681,7 @@ ContinueUnconditionally:
// e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1
// f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1
//
- // For ARM64 the scheme above is extended to support the bigger register set:
+ // For ARM64 the scheme above is extended to support the larger register set:
// - 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] for { x0-x6 }
// - 11lip010 1RRRRRRR 0RRRRRRR [[1IIIIIII] 0IIIIIII] [[1PPPPPPP] 0PPPPPPP] for { x0-x13 }
// - 11lip010 1RRRRRRR 1RRRRRRR 000RRRRR [0*2(1IIIIIII) 000IIIII] [0*2(1PPPPPPP) 000PPPPP] for { x0-x15, xip0, xip1, lr }
@@ -792,7 +793,7 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
REGDISPLAY * pContext)
{
// We could implement this unwind if we wanted, but there really isn't any reason
- ASSERT(pInfoHeader->GetReturnKind() != GCInfoHeader::MRK_ReturnsToNative);
+ ASSERT(!pInfoHeader->ReturnsToNative());
bool ebpFrame = pInfoHeader->HasFramePointer();
@@ -988,12 +989,14 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
}
PTR_UIntNative RSP = (PTR_UIntNative)rawRSP;
+ bool restoredIP = false;
if (ebpFrame)
{
pContext->pFP = RSP++;
pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location
pContext->SetIP(*RSP++); // pop the return address
+ restoredIP = true;
}
if (!pInfoHeader->AreFPLROnTop())
@@ -1002,6 +1005,8 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
ASSERT(!pInfoHeader->HasGSCookie());
}
+ ASSERT_MSG(pInfoHeader->IsFunclet() || !(dac_cast<TADDR>(RSP) & 0xf), "Callee save area must be 16-byte aligned");
+
if (saveSize > 0)
{
CalleeSavedRegMask regMask = pInfoHeader->GetSavedRegs();
@@ -1010,6 +1015,7 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
ASSERT_MSG(!ebpFrame, "Chained frame cannot have CSR_MASK_LR mask set");
pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location
pContext->SetIP(*RSP++); // pop the return address
+ restoredIP = true;
}
if (regMask & CSR_MASK_X19) { pContext->pX19 = RSP++; }
if (regMask & CSR_MASK_X20) { pContext->pX20 = RSP++; }
@@ -1024,6 +1030,12 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
if (regMask & CSR_MASK_FP ) { ASSERT(!ebpFrame); pContext->pFP = RSP++; }
}
+ if (!restoredIP)
+ {
+ pContext->SetAddrOfIP((PTR_PCODE)pContext->pLR);
+ pContext->SetIP(*pContext->pLR);
+ }
+
UInt8 vfpRegMask = (UInt8)pInfoHeader->GetVfpRegsPushedMask();
if (vfpRegMask)
{
@@ -1048,6 +1060,10 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
RSP += pInfoHeader->ParmRegsPushedCount();
+ // Excluding funclets, the total size of the callee save area and the param home area is always a multiple of 16.
+ // The compiler enforces that by placing an 8-byte padding between those areas if needed.
+ // Account for that padding and ensure that the unwound SP is 16-byte aligned.
+ RSP = dac_cast<PTR_UIntNative>((dac_cast<TADDR>(RSP) + 0xf) & ~0xf);
#else
#error NYI - For this arch
@@ -1060,7 +1076,7 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader,
PTR_VOID EECodeManager::GetReversePInvokeSaveFrame(GCInfoHeader * pHeader, REGDISPLAY * pContext)
{
- if (pHeader->GetReturnKind() != GCInfoHeader::MRK_ReturnsToNative)
+ if (!pHeader->ReturnsToNative())
return NULL;
Int32 frameOffset = pHeader->GetReversePinvokeFrameOffset();
@@ -1078,7 +1094,7 @@ UIntNative EECodeManager::GetConservativeUpperBoundForOutgoingArgs(GCInfoHeader
{
UIntNative upperBound;
- if (pInfoHeader->GetReturnKind() == GCInfoHeader::MRK_ReturnsToNative)
+ if (pInfoHeader->ReturnsToNative())
{
// Reverse PInvoke case. The embedded reverse PInvoke frame is guaranteed to reside above
// all outgoing arguments.
@@ -1098,7 +1114,9 @@ UIntNative EECodeManager::GetConservativeUpperBoundForOutgoingArgs(GCInfoHeader
#elif defined(_TARGET_ARM64_)
- PORTABILITY_ASSERT("@TODO: FIXME:ARM64");
+ // ARM64 frame pointer case. The pushed FP value is guaranteed to reside above
+ // all outgoing arguments.
+ upperBound = pContext->GetFP();
#elif defined(_TARGET_X86_)
@@ -1169,7 +1187,7 @@ PTR_PTR_VOID EECodeManager::GetReturnAddressLocationForHijack(
// We *could* hijack a reverse-pinvoke method, but it doesn't get us much because we already synchronize
// with the GC on the way back to native code.
- if (pHeader->GetReturnKind() == GCInfoHeader::MRK_ReturnsToNative)
+ if (pHeader->ReturnsToNative())
return NULL;
if (pHeader->IsFunclet())
@@ -1273,21 +1291,18 @@ GCRefKind EECodeManager::GetReturnValueKind(GCInfoHeader * pInfoHeader)
static_assert((GCRefKind)GCInfoHeader::MRK_ReturnsScalar == GCRK_Scalar, "GCInfoHeader::MRK_ReturnsScalar does not match GCRK_Scalar");
static_assert((GCRefKind)GCInfoHeader::MRK_ReturnsObject == GCRK_Object, "GCInfoHeader::MRK_ReturnsObject does not match GCRK_Object");
static_assert((GCRefKind)GCInfoHeader::MRK_ReturnsByref == GCRK_Byref, "GCInfoHeader::MRK_ReturnsByref does not match GCRK_Byref");
+#ifdef _TARGET_ARM64_
+ static_assert((GCRefKind)GCInfoHeader::MRK_Scalar_Obj == GCRK_Scalar_Obj, "GCRefKind and MethodReturnKind enumerations do not match");
+ static_assert((GCRefKind)GCInfoHeader::MRK_Obj_Obj == GCRK_Obj_Obj, "GCRefKind and MethodReturnKind enumerations do not match");
+ static_assert((GCRefKind)GCInfoHeader::MRK_Byref_Obj == GCRK_Byref_Obj, "GCRefKind and MethodReturnKind enumerations do not match");
+ static_assert((GCRefKind)GCInfoHeader::MRK_Scalar_Byref == GCRK_Scalar_Byref, "GCRefKind and MethodReturnKind enumerations do not match");
+ static_assert((GCRefKind)GCInfoHeader::MRK_Obj_Byref == GCRK_Obj_Byref, "GCRefKind and MethodReturnKind enumerations do not match");
+ static_assert((GCRefKind)GCInfoHeader::MRK_Byref_Byref == GCRK_Byref_Byref, "GCRefKind and MethodReturnKind enumerations do not match");
+#endif
GCInfoHeader::MethodReturnKind retKind = pInfoHeader->GetReturnKind();
- switch (retKind)
- {
- case GCInfoHeader::MRK_ReturnsScalar:
- case GCInfoHeader::MRK_ReturnsToNative:
- return GCRK_Scalar;
- case GCInfoHeader::MRK_ReturnsObject:
- return GCRK_Object;
- case GCInfoHeader::MRK_ReturnsByref:
- return GCRK_Byref;
- default:
- break;
- }
- UNREACHABLE_MSG("unexpected return kind");
+ ASSERT_MSG(retKind <= GCInfoHeader::MRK_LastValid, "unexpected return kind");
+ return (retKind == GCInfoHeader::MRK_ReturnsToNative) ? GCRK_Scalar : (GCRefKind)retKind;
}
bool EECodeManager::GetEpilogOffset(
@@ -1345,7 +1360,7 @@ void ** EECodeManager::GetReturnAddressLocationFromEpilog(GCInfoHeader * pInfoHe
//ASSERT(VerifyEpilogBytes(pInfoHeader, (Code *)pbEpilogStart));
// We could find the return address of a native-callable method, but it's not very useful at the moment.
- ASSERT(pInfoHeader->GetReturnKind() != GCInfoHeader::MRK_ReturnsToNative);
+ ASSERT(!pInfoHeader->ReturnsToNative());
UInt8 * pbEpilog = pbEpilogStart;
#ifdef _X86_
@@ -1796,7 +1811,7 @@ inline bool IsFramelessArm64(void)
void CheckHijackInEpilog(GCInfoHeader * pInfoHeader, Code * pEpilog, Code * pEpilogStart, UInt32 epilogSize)
{
- ASSERT(pInfoHeader->GetReturnKind() != GCInfoHeader::MRK_ReturnsToNative);
+ ASSERT(!pInfoHeader->ReturnsToNative());
if (IS_FRAMELESS())
return;
@@ -1852,8 +1867,7 @@ bool VerifyEpilogBytesX86(GCInfoHeader * pInfoHeader, Code * pEpilogStart, UInt3
Code * pEpilog = pEpilogStart;
// NativeCallable methods aren't return-address-hijacked, so we don't care about the epilog format.
- bool returnsToNative = (pInfoHeader->GetReturnKind() == GCInfoHeader::MRK_ReturnsToNative);
- if (returnsToNative)
+ if (pInfoHeader->ReturnsToNative())
return true;
if (pInfoHeader->HasFramePointer())
@@ -2047,8 +2061,7 @@ bool VerifyEpilogBytesAMD64(GCInfoHeader * pInfoHeader, Code * pEpilogStart, UIn
Code * pEpilog = pEpilogStart;
// NativeCallable methods aren't return-address-hijacked, so we don't care about the epilog format.
- bool returnsToNative = (pInfoHeader->GetReturnKind() == GCInfoHeader::MRK_ReturnsToNative);
- if (returnsToNative)
+ if (pInfoHeader->ReturnsToNative())
return true;
CHECK_HIJACK_IN_EPILOG();
@@ -2217,8 +2230,7 @@ bool VerifyEpilogBytesARM(GCInfoHeader * pInfoHeader, Code * pEpilogStart, UInt3
UInt16 * pEpilog = (UInt16 *)pEpilogStart;
// NativeCallable methods aren't return-address-hijacked, so we don't care about the epilog format.
- bool returnsToNative = (pInfoHeader->GetReturnKind() == GCInfoHeader::MRK_ReturnsToNative);
- if (returnsToNative)
+ if (pInfoHeader->ReturnsToNative())
return true;
CHECK_HIJACK_IN_EPILOG();
diff --git a/src/Native/Runtime/RhConfig.cpp b/src/Native/Runtime/RhConfig.cpp
index b36f30479..f1879ab10 100644
--- a/src/Native/Runtime/RhConfig.cpp
+++ b/src/Native/Runtime/RhConfig.cpp
@@ -68,12 +68,12 @@ UInt32 RhConfig::ReadConfigValue(_In_z_ const TCHAR *wszName, UInt32 uiDefaultVa
//reads a config value from rhconfig.ini into outputBuffer buffer returning the length of the value.
//lazily reads the file so if the file is not yet read, it will read it on first called
//if the file is not avaliable, or unreadable zero will always be returned
-//cchOuputBuffer is the maximum number of characters to write to outputBuffer
+//cchOutputBuffer is the maximum number of characters to write to outputBuffer
//cchOutputBuffer must be a size >= CONFIG_VAL_MAXLEN + 1
-UInt32 RhConfig::GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOuputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOuputBuffer)
+UInt32 RhConfig::GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer)
{
//the buffer needs to be big enough to read the value buffer + null terminator
- if (cchOuputBuffer < CONFIG_VAL_MAXLEN + 1)
+ if (cchOutputBuffer < CONFIG_VAL_MAXLEN + 1)
{
return 0;
}
@@ -99,7 +99,7 @@ UInt32 RhConfig::GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_
UInt32 iValue;
- for (iValue = 0; (iValue < CONFIG_VAL_MAXLEN + 1) && (iValue < (Int32)cchOuputBuffer); iValue++)
+ for (iValue = 0; (iValue < CONFIG_VAL_MAXLEN + 1) && (iValue < (Int32)cchOutputBuffer); iValue++)
{
outputBuffer[iValue] = ((ConfigPair*)g_iniSettings)[iSettings].Value[iValue];
diff --git a/src/Native/Runtime/RhConfig.h b/src/Native/Runtime/RhConfig.h
index c033ed6c8..677d97516 100644
--- a/src/Native/Runtime/RhConfig.h
+++ b/src/Native/Runtime/RhConfig.h
@@ -120,8 +120,8 @@ private:
//reads a config value from rhconfig.ini into outputBuffer buffer returning the length of the value.
//lazily reads the file so if the file is not yet read, it will read it on first called
//if the file is not avaliable, or unreadable zero will always be returned
- //cchOuputBuffer is the maximum number of characters to write to outputBuffer
- UInt32 GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOuputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOuputBuffer);
+ //cchOutputBuffer is the maximum number of characters to write to outputBuffer
+ UInt32 GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer);
static bool priv_isspace(char c)
{
diff --git a/src/Native/Runtime/RuntimeInstance.cpp b/src/Native/Runtime/RuntimeInstance.cpp
index 277e75c13..d2efcf3bd 100644
--- a/src/Native/Runtime/RuntimeInstance.cpp
+++ b/src/Native/Runtime/RuntimeInstance.cpp
@@ -179,6 +179,47 @@ ICodeManager * RuntimeInstance::FindCodeManagerByAddress(PTR_VOID pvAddress)
return NULL;
}
+#ifndef DACCESS_COMPILE
+
+// Find the code manager containing the given address, which might be a return address from a managed function. The
+// address may be to another managed function, or it may be to an unmanaged function. The address may also refer to
+// an EEType.
+ICodeManager * RuntimeInstance::FindCodeManagerForClasslibFunction(PTR_VOID address)
+{
+ // Try looking up the code manager assuming the address is for code first. This is expected to be most common.
+ ICodeManager * pCodeManager = FindCodeManagerByAddress(address);
+ if (pCodeManager != NULL)
+ return pCodeManager;
+
+ // Less common, we will look for the address in any of the sections of the module. This is slower, but is
+ // necessary for EEType pointers and jump stubs.
+ Module * pModule = FindModuleByAddress(address);
+ if (pModule != NULL)
+ return pModule;
+
+ ASSERT_MSG(!Thread::IsHijackTarget(address), "not expected to be called with hijacked return address");
+
+ return NULL;
+}
+
+void * RuntimeInstance::GetClasslibFunctionFromCodeAddress(PTR_VOID address, ClasslibFunctionId functionId)
+{
+ // Find the code manager for the given address, which is an address into some managed module. It could
+ // be code, or it could be an EEType. No matter what, it's an address into a managed module in some non-Rtm
+ // type system.
+ ICodeManager * pCodeManager = FindCodeManagerForClasslibFunction(address);
+
+ // If the address isn't in a managed module then we have no classlib function.
+ if (pCodeManager == NULL)
+ {
+ return NULL;
+ }
+
+ return pCodeManager->GetClasslibFunction(functionId);
+}
+
+#endif // DACCESS_COMPILE
+
PTR_UInt8 RuntimeInstance::GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC)
{
ICodeManager * pCodeManager = FindCodeManagerByAddress(ControlPC);
@@ -576,22 +617,22 @@ void RuntimeInstance::Destroy()
bool RuntimeInstance::ShouldHijackLoopForGcStress(UIntNative CallsiteIP)
{
-#if defined(FEATURE_GC_STRESS) & !defined(DACCESS_COMPILE)
+#ifdef FEATURE_GC_STRESS
return ShouldHijackForGcStress(CallsiteIP, htLoop);
-#else // FEATURE_GC_STRESS & !DACCESS_COMPILE
+#else // FEATURE_GC_STRESS
UNREFERENCED_PARAMETER(CallsiteIP);
return false;
-#endif // FEATURE_GC_STRESS & !DACCESS_COMPILE
+#endif // FEATURE_GC_STRESS
}
bool RuntimeInstance::ShouldHijackCallsiteForGcStress(UIntNative CallsiteIP)
{
-#if defined(FEATURE_GC_STRESS) & !defined(DACCESS_COMPILE)
+#ifdef FEATURE_GC_STRESS
return ShouldHijackForGcStress(CallsiteIP, htCallsite);
-#else // FEATURE_GC_STRESS & !DACCESS_COMPILE
+#else // FEATURE_GC_STRESS
UNREFERENCED_PARAMETER(CallsiteIP);
return false;
-#endif // FEATURE_GC_STRESS & !DACCESS_COMPILE
+#endif // FEATURE_GC_STRESS
}
// This method should only be called during DllMain for modules with GcStress enabled. The locking done by
@@ -889,7 +930,7 @@ COOP_PINVOKE_HELPER(void *, RhNewInterfaceDispatchCell, (EEType * pInterface, In
return NULL;
// Due to the synchronization mechanism used to update this indirection cell we must ensure the cell's alignment is twice that of a pointer.
- // Fortunately, Windows heap guarantees this aligment.
+ // Fortunately, Windows heap guarantees this alignment.
ASSERT(IS_ALIGNED(pCell, 2 * POINTER_SIZE));
ASSERT(IS_ALIGNED(pInterface, (InterfaceDispatchCell::IDC_CachePointerMask + 1)));
@@ -914,4 +955,4 @@ COOP_PINVOKE_HELPER(PTR_UInt8, RhGetThreadLocalStorageForDynamicType, (UInt32 uO
return pCurrentThread->AllocateThreadLocalStorageForDynamicType(uOffset, tlsStorageSize, numTlsCells);
}
-#endif
+#endif // DACCESS_COMPILE
diff --git a/src/Native/Runtime/RuntimeInstance.h b/src/Native/Runtime/RuntimeInstance.h
index 639a3f9a1..5bbc09651 100644
--- a/src/Native/Runtime/RuntimeInstance.h
+++ b/src/Native/Runtime/RuntimeInstance.h
@@ -14,6 +14,8 @@ enum GenericVarianceType : UInt8;
struct GenericUnificationDesc;
class GenericUnificationHashtable;
+#include "ICodeManager.h"
+
class RuntimeInstance
{
friend class AsmOffsets;
@@ -149,6 +151,8 @@ private:
bool BuildGenericTypeHashTable();
+ ICodeManager * FindCodeManagerForClasslibFunction(PTR_VOID address);
+
public:
class ModuleIterator
{
@@ -180,6 +184,7 @@ public:
void UnregisterCodeManager(ICodeManager * pCodeManager);
ICodeManager * FindCodeManagerByAddress(PTR_VOID ControlPC);
+ PTR_VOID GetClasslibFunctionFromCodeAddress(PTR_VOID address, ClasslibFunctionId functionId);
bool RegisterTypeManager(TypeManager * pTypeManager);
TypeManagerList& GetTypeManagerList();
diff --git a/src/Native/Runtime/StackFrameIterator.cpp b/src/Native/Runtime/StackFrameIterator.cpp
index 5909416d8..54773b0b5 100644
--- a/src/Native/Runtime/StackFrameIterator.cpp
+++ b/src/Native/Runtime/StackFrameIterator.cpp
@@ -250,15 +250,11 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransit
if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; }
- if (pFrame->m_Flags & PTFF_X0_IS_GCREF)
+ GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags);
+ if (retValueKind != GCRK_Scalar)
{
m_pHijackedReturnValue = (PTR_RtuObjectRef)m_RegDisplay.pX0;
- m_HijackedReturnValueKind = GCRK_Object;
- }
- if (pFrame->m_Flags & PTFF_X0_IS_BYREF)
- {
- m_pHijackedReturnValue = (PTR_RtuObjectRef)m_RegDisplay.pX0;
- m_HijackedReturnValueKind = GCRK_Byref;
+ m_HijackedReturnValueKind = retValueKind;
}
#else // _TARGET_ARM_
@@ -1728,7 +1724,7 @@ bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_RtuObjectRef * pLoca
if (GCRK_Unknown == m_HijackedReturnValueKind)
return false;
- ASSERT((GCRK_Object == m_HijackedReturnValueKind) || (GCRK_Byref == m_HijackedReturnValueKind));
+ ASSERT((GCRK_Scalar < m_HijackedReturnValueKind) && (m_HijackedReturnValueKind <= GCRK_LastValid));
*pLocation = m_pHijackedReturnValue;
*pKind = m_HijackedReturnValueKind;
@@ -1874,7 +1870,7 @@ COOP_PINVOKE_HELPER(Boolean, RhpSfiInit, (StackFrameIterator* pThis, PAL_LIMITED
// The stackwalker is intolerant to hijacked threads, as it is largely expecting to be called from C++
// where the hijack state of the thread is invariant. Because we've exposed the iterator out to C#, we
// need to unhijack every time we callback into C++ because the thread could have been hijacked during our
- // time exectuing C#.
+ // time executing C#.
pCurThread->Unhijack();
// Passing NULL is a special-case to request a standard managed stack trace for the current thread.
@@ -1894,7 +1890,7 @@ COOP_PINVOKE_HELPER(Boolean, RhpSfiNext, (StackFrameIterator* pThis, UInt32* puE
// The stackwalker is intolerant to hijacked threads, as it is largely expecting to be called from C++
// where the hijack state of the thread is invariant. Because we've exposed the iterator out to C#, we
// need to unhijack every time we callback into C++ because the thread could have been hijacked during our
- // time exectuing C#.
+ // time executing C#.
ThreadStore::GetCurrentThread()->Unhijack();
const UInt32 MaxTryRegionIdx = 0xFFFFFFFF;
diff --git a/src/Native/Runtime/amd64/GcProbe.asm b/src/Native/Runtime/amd64/GcProbe.asm
index a07e08a08..ac07653d9 100644
--- a/src/Native/Runtime/amd64/GcProbe.asm
+++ b/src/Native/Runtime/amd64/GcProbe.asm
@@ -841,16 +841,12 @@ NESTED_ENTRY RhpTrapToGC, _TEXT
sizeof_XmmAlignPad equ 8
sizeof_XmmSave equ FXSAVE_SIZE
sizeof_MachineFrame equ 6*8
- sizeof_InitialPushedArgs equ 3*8 ;; eflags, rcx, return value
+ sizeof_InitialPushedArgs equ 2*8 ;; eflags, return value
sizeof_FixedFrame equ sizeof_OutgoingScratchSpace + sizeof_PInvokeFrame + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame
;; On the stack on entry:
;; [rsp ] -> Return address
- ;; Prepare for our return by stashing a scratch register where we can pop it just before returning
- ;; The scratch register will be used as PSP in the epilog
- push rcx
-
;; save eflags before we trash them
pushfq
@@ -889,26 +885,27 @@ NESTED_ENTRY RhpTrapToGC, _TEXT
;; [rsp +2e0] | SS |
;; [rsp +2e8] | padding |
;;
- ;; [rsp +2f0] [optional stack alignment]
+ ;; [rsp +2f0] [PSP]
+ ;; [rsp +2f8] [optional stack alignment]
;;
- ;; [PSP - 18] -> eflags save
- ;; [PSP - 10] -> rcx save
+ ;; [PSP - 10] -> eflags save
;; [PSP - 8] -> Return address
;; [PSP] -> caller's frame
test rsp, 0Fh
jz AlreadyAligned
- sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 ; +8 to align RSP
+ sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 ; +8 to save PSP,
push r11 ; save incoming R11 into save location
lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 + sizeof_InitialPushedArgs]
jmp PspCalculated
AlreadyAligned:
- sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame
- push r11 ; save incoming R11 into save location
- lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + sizeof_InitialPushedArgs]
+ sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 16 ; +8 to save RSP, +8 to re-align PSP,
+ push r11 ; save incoming R11 into save location
+ lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 16 + sizeof_InitialPushedArgs]
+
PspCalculated:
push r10 ; save incoming R10 into save location
xor r10d, r10d
@@ -923,6 +920,7 @@ NESTED_ENTRY RhpTrapToGC, _TEXT
mov [rsp + 2d0h - 0a8h], r10 ; init EFLAGS to zero
mov [rsp + 2d8h - 0a8h], r11 ; save PSP in the 'machine frame'
mov [rsp + 2e0h - 0a8h], r10 ; init SS to zero
+ mov [rsp + 2f0h - 0a8h], r11 ; save PSP
.pushframe
.allocstack sizeof_XmmAlignPad + sizeof_XmmSave + 2*8 ;; only 2 of the regs from the PInvokeTransitionFrame are on the stack
@@ -1023,7 +1021,6 @@ endif ;; FEATURE_GC_STRESS
call RhpWaitForGCNoAbort
DoneWaitingForGc:
- mov rcx, rbx ; RCX <- PSP
fxrstor [rsp + 0c0h]
@@ -1064,33 +1061,34 @@ DontRestoreXmmAgain:
pop r15
pop rax ; RSP
pop rax ; RAX save
- pop rdx ; RCX save (intentionally discarding it)
+ pop rcx
pop rdx
pop r8
pop r9
pop r10
pop r11
+ ;; restore PSP
+ ;; 2F0h -> offset of the PSP area
+ ;; 0B8h -> offset of the end of the integer register area which is already popped
+ mov rsp, [rsp + 2f0h - 0b8h]
- ;; RCX is PSP at this point and the stack looks like this:
- ;; [PSP - 18] -> eflags save
- ;; [PSP - 10] -> rcx save
+ ;; RSP is PSP at this point and the stack looks like this:
+ ;; [PSP - 10] -> eflags save
;; [PSP - 8] -> return address
;; [PSP] -> caller's frame
;;
- ;; The final step is to restore eflags, rcx, and return back to the loop target location.
-
- lea rsp, [rcx - 18h]
+ ;; The final step is to restore eflags and return
+
+ lea rsp, [rsp - 10h]
jz @f ;; result of the test instruction before the pops above
popfq ;; restore flags
- pop rcx ;; restore rcx
mov rcx, STATUS_REDHAWK_THREAD_ABORT
pop rdx ;; return address as exception RIP
jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception
@@:
popfq ;; restore flags
- pop rcx ;; restore rcx
ret
NESTED_END RhpTrapToGC, _TEXT
diff --git a/src/Native/Runtime/arm64/ExceptionHandling.asm b/src/Native/Runtime/arm64/ExceptionHandling.asm
index 5df131bbe..e08118516 100644
--- a/src/Native/Runtime/arm64/ExceptionHandling.asm
+++ b/src/Native/Runtime/arm64/ExceptionHandling.asm
@@ -8,28 +8,39 @@
#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 15)&(~15))
+#define HARDWARE_EXCEPTION 1
+#define SOFTWARE_EXCEPTION 0
+
;; -----------------------------------------------------------------------------
;; Macro used to create frame of exception throwing helpers (RhpThrowEx, RhpThrowHwEx)
MACRO
- ALLOC_THROW_FRAME
+ ALLOC_THROW_FRAME $exceptionType
+
+ PROLOG_NOP mov x3, sp
;; Setup a PAL_LIMITED_CONTEXT on the stack {
- PROLOG_SAVE_REG_PAIR fp, lr, #-SIZEOF__PAL_LIMITED_CONTEXT!
- PROLOG_NOP stp x0, x1, [sp, #0x10]
+ PROLOG_STACK_ALLOC 0x50
+ IF $exceptionType == HARDWARE_EXCEPTION
+ PROLOG_NOP stp x3, x1, [sp] ; x3 is the SP and x1 is the IP of the fault site
+ PROLOG_PUSH_MACHINE_FRAME
+ ELSE
+ PROLOG_NOP stp x3, lr, [sp] ; x3 is the SP and lr is the IP of the fault site
+ ENDIF
+ PROLOG_NOP stp d8, d9, [sp, #0x10]
+ PROLOG_NOP stp d10, d11, [sp, #0x20]
+ PROLOG_NOP stp d12, d13, [sp, #0x30]
+ PROLOG_NOP stp d14, d15, [sp, #0x40]
+ PROLOG_SAVE_REG_PAIR fp, lr, #-0x70!
+ PROLOG_NOP stp xzr, xzr, [sp, #0x10] ; locations reserved for return value, not used for exception handling
PROLOG_SAVE_REG_PAIR x19, x20, #0x20
PROLOG_SAVE_REG_PAIR x21, x22, #0x30
PROLOG_SAVE_REG_PAIR x23, x24, #0x40
PROLOG_SAVE_REG_PAIR x25, x26, #0x50
PROLOG_SAVE_REG_PAIR x27, x28, #0x60
- PROLOG_NOP stp x0, lr, [sp, #0x70] ; x0 is the SP and lr is the IP of the fault site
- PROLOG_NOP stp d8, d9, [sp, #0x80]
- PROLOG_NOP stp d10, d11, [sp, #0x90]
- PROLOG_NOP stp d12, d13, [sp, #0xA0]
- PROLOG_NOP stp d14, d15, [sp, #0xB0]
;; } end PAL_LIMITED_CONTEXT
-
+
PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo
- MEND
+ MEND
;; -----------------------------------------------------------------------------
;; Macro used to create frame of funclet calling helpers (RhpCallXXXXFunclet)
@@ -199,19 +210,7 @@
#define rsp_offsetof_ExInfo 0
#define rsp_offsetof_Context STACKSIZEOF_ExInfo
- PROLOG_NOP mov w2, w0 ;; save exception code into x2
- PROLOG_NOP mov x0, sp ;; get SP of fault site
-
- PROLOG_NOP mov lr, x1 ;; set IP of fault site
-
- ALLOC_THROW_FRAME
-
- ; x0: SP of fault site
- ; x1: IP of fault site
- ; x2: exception code of fault
- ; lr: IP of fault site (as a 'return address')
-
- mov w0, w2 ;; w0 <- exception code of fault
+ ALLOC_THROW_FRAME HARDWARE_EXCEPTION
;; x2 = GetThread(), TRASHES x1
INLINE_GETTHREAD x2, x1
@@ -256,11 +255,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpThrowEx
- ALLOC_THROW_FRAME
-
- ;; Compute and save SP at callsite.
- add x1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT)
- str x1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)]
+ ALLOC_THROW_FRAME SOFTWARE_EXCEPTION
;; x2 = GetThread(), TRASHES x1
INLINE_GETTHREAD x2, x1
@@ -351,11 +346,7 @@ NotHijacked
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpRethrow
- ALLOC_THROW_FRAME
-
- ;; Compute and save SP at callsite.
- add x1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT)
- str x1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)]
+ ALLOC_THROW_FRAME SOFTWARE_EXCEPTION
;; x2 = GetThread(), TRASHES x1
INLINE_GETTHREAD x2, x1
@@ -599,7 +590,11 @@ SetSuccess
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpCallFilterFunclet
- ALLOC_CALL_FUNCLET_FRAME 0
+ ALLOC_CALL_FUNCLET_FRAME 0x40
+ stp d8, d9, [sp, #0x00]
+ stp d10, d11, [sp, #0x10]
+ stp d12, d13, [sp, #0x20]
+ stp d14, d15, [sp, #0x30]
ldr x12, [x2, #OFFSETOF__REGDISPLAY__pFP]
ldr fp, [x12]
@@ -612,7 +607,12 @@ SetSuccess
EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2
- FREE_CALL_FUNCLET_FRAME 0
+ ldp d8, d9, [sp, #0x00]
+ ldp d10, d11, [sp, #0x10]
+ ldp d12, d13, [sp, #0x20]
+ ldp d14, d15, [sp, #0x30]
+
+ FREE_CALL_FUNCLET_FRAME 0x40
EPILOG_RETURN
NESTED_END RhpCallFilterFunclet
diff --git a/src/Native/Runtime/arm64/GcProbe.asm b/src/Native/Runtime/arm64/GcProbe.asm
index a6ee43c56..051b4158d 100644
--- a/src/Native/Runtime/arm64/GcProbe.asm
+++ b/src/Native/Runtime/arm64/GcProbe.asm
@@ -20,7 +20,7 @@
PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH
;; Build a map of symbols representing offsets into the transition frame (see PInvokeTransitionFrame in
- ;; rhbinder.h and keep these two in sync.
+ ;; rhbinder.h) and keep these two in sync.
map 0
field OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs
field 10 * 8 ; x19..x28
@@ -41,7 +41,7 @@ PROBE_FRAME_SIZE field 0
;;
;; Note that we currently employ a significant simplification of frame setup: we always allocate a
;; maximally-sized PInvokeTransitionFrame and save all of the registers. Depending on the caller this can
- ;; lead to upto 20 additional register saves (x0-x18, lr) or 160 bytes of stack space. I have done no
+ ;; lead to up to 20 additional register saves (x0-x18, lr) or 160 bytes of stack space. I have done no
;; analysis to see whether any of the worst cases occur on performance sensitive paths and whether the
;; additional saves will show any measurable degradation.
@@ -82,7 +82,7 @@ PROBE_FRAME_SIZE field 0
; Save the floating return registers
PROLOG_NOP stp d0, d1, [sp, #0x120]
- PROLOG_NOP stp d2, d3, [sp, #0x130]
+ PROLOG_NOP stp d2, d3, [sp, #0x130]
MEND
@@ -106,9 +106,9 @@ PROBE_FRAME_SIZE field 0
; Restore the floating return registers
EPILOG_NOP ldp d0, d1, [sp, #0x120]
- EPILOG_NOP ldp d2, d3, [sp, #0x130]
+ EPILOG_NOP ldp d2, d3, [sp, #0x130]
- ;; Resttore callee saved registers
+ ;; Restore callee saved registers
EPILOG_RESTORE_REG_PAIR x19, x20, #0x20
EPILOG_RESTORE_REG_PAIR x21, x22, #0x30
EPILOG_RESTORE_REG_PAIR x23, x24, #0x40
@@ -135,12 +135,13 @@ BitmaskStr SETS "$savedRegsMask"
str $threadReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_pThread] ; Thread *
IF BitmaskStr:LEFT:1 == "#"
- ;; The savedRegsMask is a constant, remove the leading "#" since the MOVL64 doesn't expect it
-BitmaskStr SETS BitmaskStr:RIGHT:(:LEN:BitmaskStr - 1)
- MOVL64 $trashReg, $BitmaskStr, $gcFlags
+ ;; The savedRegsMask is a constant, remove the leading "#" since the MOVL64 doesn't expect it
+BitmaskStr SETS BitmaskStr:RIGHT:(:LEN:BitmaskStr - 1)
+ MOVL64 $trashReg, $BitmaskStr, $gcFlags
ELSE
- ;; The savedRegsMask is a register
- mov $trashReg, $savedRegsMask
+ ASSERT "$gcFlags" == ""
+ ;; The savedRegsMask is a register
+ mov $trashReg, $savedRegsMask
ENDIF
str $trashReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags]
add $trashReg, sp, #$frameSize
@@ -178,7 +179,7 @@ __PPF_ThreadReg SETS "x2"
; Perform the rest of the PInvokeTransitionFrame initialization.
INIT_PROBE_FRAME $__PPF_ThreadReg, $trashReg, $savedRegsMask, $gcFlags, PROBE_FRAME_SIZE
- add $trashReg, sp, xzr
+ mov $trashReg, sp
str $trashReg, [$__PPF_ThreadReg, #OFFSETOF__Thread__m_pHackPInvokeTunnel]
MEND
@@ -255,8 +256,11 @@ EXTRA_SAVE_SIZE equ (28*8)
MACRO
ClearHijackState
- str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
- str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
+ ASSERT OFFSETOF__Thread__m_pvHijackedReturnAddress == (OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8)
+ ;; Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress
+ stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
+ ;; Clear m_uHijackedReturnValueFlags
+ str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags]
MEND
;;
@@ -269,6 +273,7 @@ EXTRA_SAVE_SIZE equ (28*8)
;; Register state on exit:
;; x2: thread pointer
;; x3: trashed
+;; x12: transition frame flags for the return registers x0 and x1
;;
MACRO
FixupHijackedCallstack
@@ -279,7 +284,9 @@ EXTRA_SAVE_SIZE equ (28*8)
;;
;; Fix the stack by restoring the original return address
;;
- ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
+ ASSERT OFFSETOF__Thread__m_uHijackedReturnValueFlags == (OFFSETOF__Thread__m_pvHijackedReturnAddress + 8)
+ ;; Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags
+ ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
ClearHijackState
MEND
@@ -330,39 +337,15 @@ EXTRA_SAVE_SIZE equ (28*8)
;;
EXTERN RhpPInvokeExceptionGuard
-
- NESTED_ENTRY RhpGcProbeHijackScalarWrapper, .text, RhpPInvokeExceptionGuard
- brk 0xf000 ;; TODO: remove after debugging/testing stub
- HijackTargetFakeProlog
-
- LABELED_RETURN_ADDRESS RhpGcProbeHijackScalar
-
- FixupHijackedCallstack
- MOVL64 x12, DEFAULT_FRAME_SAVE_FLAGS, 0
- b RhpGcProbe
- NESTED_END RhpGcProbeHijackScalarWrapper
-
- NESTED_ENTRY RhpGcProbeHijackObjectWrapper, .text, RhpPInvokeExceptionGuard
- brk 0xf000 ;; TODO: remove after debugging/testing stub
- HijackTargetFakeProlog
-
- LABELED_RETURN_ADDRESS RhpGcProbeHijackObject
-
- FixupHijackedCallstack
- MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI
- b RhpGcProbe
- NESTED_END RhpGcProbeHijackObjectWrapper
-
- NESTED_ENTRY RhpGcProbeHijackByrefWrapper, .text, RhpPInvokeExceptionGuard
- brk 0xf000 ;; TODO: remove after debugging/testing stub
+ NESTED_ENTRY RhpGcProbeHijackWrapper, .text, RhpPInvokeExceptionGuard
HijackTargetFakeProlog
- LABELED_RETURN_ADDRESS RhpGcProbeHijackByref
+ LABELED_RETURN_ADDRESS RhpGcProbeHijack
FixupHijackedCallstack
- MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0) , PTFF_X0_IS_BYREF_HI
+ orr x12, x12, #DEFAULT_FRAME_SAVE_FLAGS
b RhpGcProbe
- NESTED_END RhpGcProbeHijackByrefWrapper
+ NESTED_END RhpGcProbeHijackWrapper
#ifdef FEATURE_GC_STRESS
;;
@@ -370,25 +353,11 @@ EXTRA_SAVE_SIZE equ (28*8)
;; GC Stress Hijack targets
;;
;;
- LEAF_ENTRY RhpGcStressHijackScalar
- FixupHijackedCallstack
- MOVL64 x12, DEFAULT_FRAME_SAVE_FLAGS, 0
- b RhpGcStressProbe
- LEAF_END RhpGcStressHijackScalar
-
- LEAF_ENTRY RhpGcStressHijackObject
- FixupHijackedCallstack
- MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI
- b RhpGcStressProbe
- LEAF_END RhpGcStressHijackObject
-
- LEAF_ENTRY RhpGcStressHijackByref
+ LEAF_ENTRY RhpGcStressHijack
FixupHijackedCallstack
- MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_BYREF_HI
+ orr x12, x12, #DEFAULT_FRAME_SAVE_FLAGS
b RhpGcStressProbe
- LEAF_END RhpGcStressHijackByref
-
-
+ LEAF_END RhpGcStressHijack
;;
;; Worker for our GC stress probes. Do not call directly!!
;; Instead, go through RhpGcStressHijack{Scalar|Object|Byref}.
@@ -595,7 +564,7 @@ EXTRA_SAVE_SIZE equ (28*8)
#endif ;; FEATURE_GC_STRESS
-
+#if 0 // used by the binder only
;;
;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH
;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing
@@ -605,10 +574,10 @@ EXTRA_SAVE_SIZE equ (28*8)
;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the
;; handler in the caller.
;;
-;; If we are hijacked, then we jump to a routine that will unhijack appropriatley and wait for the GC to
+;; If we are hijacked, then we jump to a routine that will unhijack appropriately and wait for the GC to
;; complete. There are also variants for GC stress.
;;
-;; Note that at this point we are eiher hijacked or we are not, and this will not change until we return to
+;; Note that at this point we are either hijacked or we are not, and this will not change until we return to
;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack
;; another thread while the target thread is suspended in managed code, and this is _not_ managed code.
;;
@@ -634,13 +603,13 @@ EXTRA_SAVE_SIZE equ (28*8)
MEND
;; We need an instance of the helper for each possible hijack function. The binder has enough
;; information to determine which one we need to use for any function.
- RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijackScalar, {false}, 0
- RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijackObject, {false}, 0
- RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijackByref, {false}, 0
+ RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijack, {false}, 0
+ RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijack, {false}, 0
+ RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijack, {false}, 0
#ifdef FEATURE_GC_STRESS
- RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijackScalar, {true}, RhpGcStressHijackScalar
- RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijackObject, {true}, RhpGcStressHijackObject
- RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijackByref, {true}, RhpGcStressHijackByref
+ RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
+ RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
+ RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
#endif
;;
@@ -757,7 +726,10 @@ EXTRA_SAVE_SIZE equ (28*8)
EHJumpProbeEpilog
NESTED_END RhpGCStressProbeForEHJump
+#endif ;; FEATURE_GC_STRESS
+#endif ;; 0
+#ifdef FEATURE_GC_STRESS
;;
;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this.
;;
diff --git a/src/Native/Runtime/arm64/PInvoke.asm b/src/Native/Runtime/arm64/PInvoke.asm
index c05328314..01decb3c5 100644
--- a/src/Native/Runtime/arm64/PInvoke.asm
+++ b/src/Native/Runtime/arm64/PInvoke.asm
@@ -23,12 +23,13 @@
;; FP and LR registers
PROLOG_SAVE_REG_PAIR fp, lr, #-0xA0! ;; Push down stack pointer and store FP and LR
- ;; Need to save argument registers x0-x7 and the return buffer register x8 (twice just for 16B alignment)
+ ;; Need to save argument registers x0-x7 and the return buffer register x8
+ ;; Also save x9 which may be used for saving indirect call target
stp x0, x1, [sp, #0x10]
stp x2, x3, [sp, #0x20]
stp x4, x5, [sp, #0x30]
stp x6, x7, [sp, #0x40]
- stp x8, x8, [sp, #0x50]
+ stp x8, x9, [sp, #0x50]
;; Save float argument registers as well since they're volatile
stp d0, d1, [sp, #0x60]
@@ -49,7 +50,7 @@
ldp x2, x3, [sp, #0x20]
ldp x4, x5, [sp, #0x30]
ldp x6, x7, [sp, #0x40]
- ldr x8, [sp, #0x50]
+ ldp x8, x9, [sp, #0x50]
;; Restore FP and LR registers, and free the allocated stack block
EPILOG_RESTORE_REG_PAIR fp, lr, #0xA0!
diff --git a/src/Native/Runtime/arm64/UniversalTransition.asm b/src/Native/Runtime/arm64/UniversalTransition.asm
index 4c21397f7..fe4763740 100644
--- a/src/Native/Runtime/arm64/UniversalTransition.asm
+++ b/src/Native/Runtime/arm64/UniversalTransition.asm
@@ -110,7 +110,7 @@
stp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)]
stp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)]
stp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)]
- str x8, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)]
+ stp x8, xzr, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)]
#ifdef TRASH_SAVED_ARGUMENT_REGISTERS
;; ARM64TODO
diff --git a/src/Native/Runtime/coreclr/gcinfodecoder.cpp b/src/Native/Runtime/coreclr/gcinfodecoder.cpp
index a7bc3d204..266e7045a 100644
--- a/src/Native/Runtime/coreclr/gcinfodecoder.cpp
+++ b/src/Native/Runtime/coreclr/gcinfodecoder.cpp
@@ -166,7 +166,7 @@ GcInfoDecoder::GcInfoDecoder(
if (hasGSCookie)
{
// Note that normalization as a code offset can be different than
- // normalization as code legnth
+ // normalization as code length
UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
// Decode prolog/epilog information
@@ -330,7 +330,7 @@ GcInfoDecoder::GcInfoDecoder(
else if(flags & DECODE_FOR_RANGES_CALLBACK)
{
// Note that normalization as a code offset can be different than
- // normalization as code legnth
+ // normalization as code length
UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
UINT32 numBitsPerOffset = CeilOfLog2(normCodeLength);
@@ -453,7 +453,7 @@ void GcInfoDecoder::EnumerateInterruptibleRanges (
EnumerateInterruptibleRangesCallback *pCallback,
void * hCallback)
{
- // If no info is found for the call site, we default to fully-interruptbile
+ // If no info is found for the call site, we default to fully-interruptible
LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset));
UINT32 lastInterruptibleRangeStopOffsetNormalized = 0;
@@ -793,7 +793,7 @@ bool GcInfoDecoder::EnumerateLiveSlots(
_ASSERTE(m_NumInterruptibleRanges);
_ASSERTE(numInterruptibleLength);
- // If no info is found for the call site, we default to fully-interruptbile
+ // If no info is found for the call site, we default to fully-interruptible
LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset));
UINT32 numChunks = (numInterruptibleLength + NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
diff --git a/src/Native/Runtime/gcdump.cpp b/src/Native/Runtime/gcdump.cpp
index 20eeae53c..d5022b44c 100644
--- a/src/Native/Runtime/gcdump.cpp
+++ b/src/Native/Runtime/gcdump.cpp
@@ -42,7 +42,21 @@ GCDump::GCDump()
static const char * const calleeSaveRegMaskBitNumberToName[] =
{
-#ifdef _ARM_
+#if defined(_TARGET_X86_)
+ "EBX",
+ "ESI",
+ "EDI",
+ "EBP",
+#elif defined(_TARGET_AMD64_)
+ "RBX",
+ "RSI",
+ "RDI",
+ "RBP",
+ "R12",
+ "R13",
+ "R14",
+ "R15"
+#elif defined(_TARGET_ARM_)
"R4",
"R5",
"R6",
@@ -52,18 +66,72 @@ static const char * const calleeSaveRegMaskBitNumberToName[] =
"R10",
"R11",
"LR",
-#else // _ARM_
- "EBX",
- "ESI",
- "EDI",
- "EBP",
- "R12",
- "R13",
- "R14",
- "R15"
-#endif // _ARM_
+#elif defined(_TARGET_ARM64_)
+ "LR",
+ "X19",
+ "X20",
+ "X21",
+ "X22",
+ "X23",
+ "X24",
+ "X25",
+ "X26",
+ "X27",
+ "X28",
+ "FP",
+#else
+#error unknown architecture
+#endif
};
+char const * GetReturnKindString(GCInfoHeader::MethodReturnKind returnKind)
+{
+ switch (returnKind)
+ {
+ case GCInfoHeader::MRK_ReturnsScalar: return "scalar";
+ case GCInfoHeader::MRK_ReturnsObject: return "object";
+ case GCInfoHeader::MRK_ReturnsByref: return "byref";
+ case GCInfoHeader::MRK_ReturnsToNative: return "native";
+#if defined(_TARGET_ARM64_)
+ case GCInfoHeader::MRK_Scalar_Obj: return "{scalar, object}";
+ case GCInfoHeader::MRK_Obj_Obj: return "{object, object}";
+ case GCInfoHeader::MRK_Byref_Obj: return "{byref, object}";
+ case GCInfoHeader::MRK_Scalar_Byref: return "{scalar, byref}";
+ case GCInfoHeader::MRK_Obj_Byref: return "{object, byref}";
+ case GCInfoHeader::MRK_Byref_Byref: return "{byref, byref}";
+#endif // defined(_TARGET_ARM64_)
+ default: return "???";
+ }
+}
+
+char const * GetFramePointerRegister()
+{
+#if defined(_TARGET_X86_)
+ return "EBP";
+#elif defined(_TARGET_AMD64_)
+ return "RBP";
+#elif defined(_TARGET_ARM_)
+ return "R7";
+#elif defined(_TARGET_ARM64_)
+ return "FP";
+#else
+#error unknown architecture
+#endif
+}
+
+char const * GetStackPointerRegister()
+{
+#if defined(_TARGET_X86_)
+ return "ESP";
+#elif defined(_TARGET_AMD64_)
+ return "RSP";
+#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+ return "SP";
+#else
+#error unknown architecture
+#endif
+}
+
size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo,
Tables * pTables,
GCInfoHeader * pHeader /* OUT */
@@ -105,23 +173,8 @@ size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo,
gcPrintf(" epilogSize: %d\n", pHeader->GetFixedEpilogSize());
gcPrintf(" epilogCount: %d %s\n", epilogCount, epilogAtEnd ? "[end]" : "");
- const char * returnKind = "????";
- unsigned reversePinvokeFrameOffset = 0; // it can't be 0 because [ebp+0] is the previous ebp
- switch (pHeader->GetReturnKind())
- {
- case GCInfoHeader::MRK_ReturnsScalar: returnKind = "scalar"; break;
- case GCInfoHeader::MRK_ReturnsObject: returnKind = "object"; break;
- case GCInfoHeader::MRK_ReturnsByref: returnKind = "byref"; break;
- case GCInfoHeader::MRK_ReturnsToNative:
- returnKind = "to native";
- reversePinvokeFrameOffset = pHeader->GetReversePinvokeFrameOffset();
- break;
- case GCInfoHeader::MRK_Unknown:
- //ASSERT("Unexpected return kind")
- break;
- }
- gcPrintf(" returnKind: %s\n", returnKind);
- gcPrintf(" frameKind: %s", pHeader->HasFramePointer() ? "EBP" : "ESP");
+ gcPrintf(" returnKind: %s\n", GetReturnKindString(pHeader->GetReturnKind()));
+ gcPrintf(" frameKind: %s", pHeader->HasFramePointer() ? GetFramePointerRegister() : GetStackPointerRegister());
#ifdef _TARGET_AMD64_
if (pHeader->HasFramePointer())
gcPrintf(" offset: %d", pHeader->GetFramePointerOffset());
@@ -162,13 +215,13 @@ size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo,
}
#endif
- if (reversePinvokeFrameOffset != 0)
+ if (pHeader->ReturnsToNative())
{
- gcPrintf(" reversePinvokeFrameOffset: 0x%02x\n", reversePinvokeFrameOffset);
+ gcPrintf(" reversePinvokeFrameOffset: 0x%02x\n", pHeader->GetReversePinvokeFrameOffset());
}
- if (!epilogAtEnd || (epilogCount > 2))
+ if (!epilogAtEnd && !pHeader->IsFunclet())
{
gcPrintf(" epilog offsets: ");
unsigned previousOffset = 0;
@@ -186,31 +239,92 @@ size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo,
return gcInfo - gcInfoStart;
}
+// TODO: Can we unify this code with ReportLocalSlot in RHCodeMan.cpp?
void GCDump::PrintLocalSlot(UInt32 slotNum, GCInfoHeader const * pHeader)
{
- // @TODO: print both EBP/ESP offsets where appropriate
+ char const * baseReg;
+ Int32 offset;
+
+ if (pHeader->HasFramePointer())
+ {
+ baseReg = GetFramePointerRegister();
#ifdef _TARGET_ARM_
- gcPrintf("local slot 0n%d, [R7+%02X] \n", slotNum,
- ((GCInfoHeader*)pHeader)->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE));
+ offset = pHeader->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE);
+#elif defined(_TARGET_ARM64_)
+ if (pHeader->AreFPLROnTop())
+ {
+ offset = -(Int32)((slotNum + 1) * POINTER_SIZE);
+ }
+ else
+ {
+ offset = (slotNum + 2) * POINTER_SIZE;
+ }
+#elif defined(_TARGET_X86_)
+ offset = -pHeader->GetPreservedRegsSaveSize() - (slotNum * POINTER_SIZE);
+#elif defined(_TARGET_AMD64_)
+ if (pHeader->GetFramePointerOffset() == 0)
+ {
+ offset = -pHeader->GetPreservedRegsSaveSize() - (slotNum * POINTER_SIZE);
+ }
+ else
+ {
+ offset = (slotNum * POINTER_SIZE);
+ }
#else
- const char* regAndSign = "EBP-";
- size_t offset = pHeader->GetPreservedRegsSaveSize() + (slotNum * POINTER_SIZE);
-# ifdef _TARGET_AMD64_
- if (((GCInfoHeader*)pHeader)->GetFramePointerOffset() == 0)
- {
- regAndSign = "RBP-";
+#error unknown architecture
+#endif
}
else
{
- regAndSign = "RBP+";
- offset = (slotNum * POINTER_SIZE);
+ baseReg = GetStackPointerRegister();
+ offset = pHeader->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE);
+ }
+
+ char const * sign = "+";
+ if (offset < 0)
+ {
+ sign = "-";
+ offset = -offset;
+ }
+ gcPrintf("local slot 0n%d, [%s%s%02X]\n", slotNum, baseReg, sign, offset);
+}
+
+// Reads a 7-bit-encoded register mask:
+// - 0RRRRRRR for non-ARM64 registers and { x0-x6 } ARM64 registers
+// - 1RRRRRRR 0RRRRRRR for { x0-x13 } ARM64 registers
+// - 1RRRRRRR 1RRRRRRR 000RRRRR for { x0-x15, xip0, xip1, lr } ARM64 registers
+// Returns the number of bytes read.
+size_t ReadRegisterMaskBy7Bit(PTR_UInt8 pCursor, UInt32* pMask)
+{
+ UInt32 byte0 = *pCursor;
+ if (!(byte0 & 0x80))
+ {
+ *pMask = byte0;
+ return 1;
+ }
+
+#if defined(_TARGET_ARM64_)
+ UInt32 byte1 = *(pCursor + 1);
+ if (!(byte1 & 0x80))
+ {
+ // XOR with 0x80 discards the most significant bit of byte0
+ *pMask = (byte1 << 7) ^ byte0 ^ 0x80;
+ return 2;
+ }
+
+ UInt32 byte2 = *(pCursor + 2);
+ if (!(byte2 & 0x80))
+ {
+ // XOR with 0x4080 discards the most significant bits of byte0 and byte1
+ *pMask = (byte2 << 14) ^ (byte1 << 7) ^ byte0 ^ 0x4080;
+ return 3;
}
-# endif
- gcPrintf("local slot 0n%d, [%s%02X] \n", slotNum, regAndSign, offset);
#endif
+
+ UNREACHABLE_MSG("Register mask is too long");
}
-void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteString,
+void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteString,
GCInfoHeader const * pHeader)
{
gcPrintf("%04x: ", callsiteOffset);
@@ -245,24 +359,35 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin
if (b & CSR_MASK_R7) { gcPrintf("R7 "); count++; }
if (b & CSR_MASK_R8) { gcPrintf("R8 "); count++; }
#elif defined(_TARGET_ARM64_)
- // ARM64TODO: not all of these are needed?
- if (b & CSR_MASK_X19) { gcPrintf("X19 "); count++; }
- if (b & CSR_MASK_X20) { gcPrintf("X20 "); count++; }
- if (b & CSR_MASK_X21) { gcPrintf("X21 "); count++; }
- if (b & CSR_MASK_X22) { gcPrintf("X22 "); count++; }
- if (b & CSR_MASK_X23) { gcPrintf("X23 "); count++; }
- if (b & CSR_MASK_X24) { gcPrintf("X24 "); count++; }
- if (b & CSR_MASK_X25) { gcPrintf("X25 "); count++; }
- if (b & CSR_MASK_X26) { gcPrintf("X26 "); count++; }
- if (b & CSR_MASK_X27) { gcPrintf("X27 "); count++; }
- if (b & CSR_MASK_X28) { gcPrintf("X28 "); count++; }
-#else // _ARM_
+ UInt16 regs = (b & 0xF);
+ if (b & 0x10) { regs |= (*pCursor++ << 4); }
+
+ ASSERT(!(regs & CSR_MASK_LR));
+ if (regs & CSR_MASK_X19) { gcPrintf("X19 "); count++; }
+ if (regs & CSR_MASK_X20) { gcPrintf("X20 "); count++; }
+ if (regs & CSR_MASK_X21) { gcPrintf("X21 "); count++; }
+ if (regs & CSR_MASK_X22) { gcPrintf("X22 "); count++; }
+ if (regs & CSR_MASK_X23) { gcPrintf("X23 "); count++; }
+ if (regs & CSR_MASK_X24) { gcPrintf("X24 "); count++; }
+ if (regs & CSR_MASK_X25) { gcPrintf("X25 "); count++; }
+ if (regs & CSR_MASK_X26) { gcPrintf("X26 "); count++; }
+ if (regs & CSR_MASK_X27) { gcPrintf("X27 "); count++; }
+ if (regs & CSR_MASK_X28) { gcPrintf("X28 "); count++; }
+ if (regs & CSR_MASK_FP ) { gcPrintf("FP " ); count++; }
+#elif defined(_TARGET_AMD64_)
if (b & CSR_MASK_RBX) { gcPrintf("RBX "); count++; }
if (b & CSR_MASK_RSI) { gcPrintf("RSI "); count++; }
if (b & CSR_MASK_RDI) { gcPrintf("RDI "); count++; }
if (b & CSR_MASK_RBP) { gcPrintf("RBP "); count++; }
if (b & CSR_MASK_R12) { gcPrintf("R12 "); count++; }
-#endif // _ARM_
+#elif defined(_TARGET_X86_)
+ if (b & CSR_MASK_RBX) { gcPrintf("EBX "); count++; }
+ if (b & CSR_MASK_RSI) { gcPrintf("ESI "); count++; }
+ if (b & CSR_MASK_RDI) { gcPrintf("EDI "); count++; }
+ if (b & CSR_MASK_RBP) { gcPrintf("EBP "); count++; }
+#else
+#error unknown architecture
+#endif
gcPrintf("\n");
}
break;
@@ -293,21 +418,32 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin
case CSR_NUM_X23: regName = "X23"; break;
case CSR_NUM_X24: regName = "X24"; break;
case CSR_NUM_X25: regName = "X25"; break;
- case CSR_NUM_X26: regName = "X26"; break;
- case CSR_NUM_X27: regName = "X27"; break;
- case CSR_NUM_X28: regName = "X28"; break;
-#else // _ARM_
+ case 0:
+ switch (*pCursor++)
+ {
+ case CSR_NUM_X26: regName = "X26"; break;
+ case CSR_NUM_X27: regName = "X27"; break;
+ case CSR_NUM_X28: regName = "X28"; break;
+ case CSR_NUM_FP : regName = "FP" ; break;
+ }
+ break;
+#elif defined(_TARGET_AMD64_)
case CSR_NUM_RBX: regName = "RBX"; break;
case CSR_NUM_RSI: regName = "RSI"; break;
case CSR_NUM_RDI: regName = "RDI"; break;
case CSR_NUM_RBP: regName = "RBP"; break;
-#ifdef _TARGET_AMD64_
case CSR_NUM_R12: regName = "R12"; break;
case CSR_NUM_R13: regName = "R13"; break;
case CSR_NUM_R14: regName = "R14"; break;
case CSR_NUM_R15: regName = "R15"; break;
-#endif // _TARGET_AMD64_
-#endif // _ARM_
+#elif defined(_TARGET_X86_)
+ case CSR_NUM_RBX: regName = "EBX"; break;
+ case CSR_NUM_RSI: regName = "ESI"; break;
+ case CSR_NUM_RDI: regName = "EDI"; break;
+ case CSR_NUM_RBP: regName = "EBP"; break;
+#else
+#error unknown architecture
+#endif
}
gcPrintf("%02x | 3 %s%s%s \n", b, regName, interior, pinned);
count++;
@@ -318,30 +454,41 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin
{
if (b & 0x10)
{
- // case 4 -- "local slot set"
- gcPrintf("%02x | 4 ", b);
- bool isFirst = true;
-
- int mask = 0x01;
- int slotNum = 0;
- while (mask <= 0x08)
+ // case 4 -- "local slot set" or "common var tail"
+ if ((b & 0x0f) != 0)
{
- if (b & mask)
+ gcPrintf("%02x | 4 ", b);
+ bool isFirst = true;
+
+ int mask = 0x01;
+ int slotNum = 0;
+ while (mask <= 0x08)
{
- if (!isFirst)
+ if (b & mask)
{
- if (!first)
- gcPrintf(" ");
- gcPrintf(" | ");
- }
+ if (!isFirst)
+ {
+ if (!first)
+ gcPrintf(" ");
+ gcPrintf(" | ");
+ }
- PrintLocalSlot(slotNum, pHeader);
+ PrintLocalSlot(slotNum, pHeader);
- isFirst = false;
- count++;
+ isFirst = false;
+ count++;
+ }
+ mask <<= 1;
+ slotNum++;
}
- mask <<= 1;
- slotNum++;
+ }
+ else
+ {
+ unsigned commonVarInx = 0;
+ if ((b & 0x20) == 0)
+ commonVarInx = VarInt::ReadUnsigned(pCursor);
+
+ gcPrintf("%02x | 8 set #%04u\n", b, commonVarInx);
}
}
else
@@ -357,50 +504,124 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin
break;
case 0xC0:
{
- gcPrintf("%02x ", b);
- unsigned mask = 0;
- PTR_UInt8 pInts = pCursor;
- unsigned offset = VarInt::ReadUnsigned(pCursor);
- const char* interior = (b & 0x10) ? "+" : "";
- const char* pinned = (b & 0x08) ? "!" : "";
-#ifdef _TARGET_ARM_
- const char* baseReg = (b & 0x04) ? "R7" : "SP";
+ if ((b & 0xC7) == 0xC2)
+ {
+ // case 7 - live scratch regs
+ gcPrintf("%02x | 7 ", b);
+
+ UInt32 regs, byrefRegs = 0, pinnedRegs = 0;
+ pCursor += ReadRegisterMaskBy7Bit(pCursor, &regs);
+ if (b & 0x10)
+ pCursor += ReadRegisterMaskBy7Bit(pCursor, &byrefRegs);
+ if (b & 0x08)
+ pCursor += ReadRegisterMaskBy7Bit(pCursor, &pinnedRegs);
+
+ for (UInt32 reg = 0; ; reg++)
+ {
+ UInt32 regMask = (1 << reg);
+ if (regMask > regs)
+ break;
+
+ if (regs & regMask)
+ {
+ char* pinned = (pinnedRegs & regMask) ? "!" : "";
+ char* interior = (byrefRegs & regMask) ? "+" : "";
+ char* regStr = "???";
+
+ switch (reg)
+ {
+#if defined(_TARGET_ARM_)
+ case SR_NUM_R0: regStr = "R0"; break;
+ case SR_NUM_R1: regStr = "R1"; break;
+ case SR_NUM_R2: regStr = "R2"; break;
+ case SR_NUM_R3: regStr = "R3"; break;
+ case SR_NUM_R12: regStr = "R12"; break;
+ case SR_NUM_LR: regStr = "LR"; break;
+#elif defined(_TARGET_ARM64_)
+ case SR_NUM_X0: regStr = "X0"; break;
+ case SR_NUM_X1: regStr = "X1"; break;
+ case SR_NUM_X2: regStr = "X2"; break;
+ case SR_NUM_X3: regStr = "X3"; break;
+ case SR_NUM_X4: regStr = "X4"; break;
+ case SR_NUM_X5: regStr = "X5"; break;
+ case SR_NUM_X6: regStr = "X6"; break;
+ case SR_NUM_X7: regStr = "X7"; break;
+ case SR_NUM_X8: regStr = "X8"; break;
+ case SR_NUM_X9: regStr = "X9"; break;
+ case SR_NUM_X10: regStr = "X10"; break;
+ case SR_NUM_X11: regStr = "X11"; break;
+ case SR_NUM_X12: regStr = "X12"; break;
+ case SR_NUM_X13: regStr = "X13"; break;
+ case SR_NUM_X14: regStr = "X14"; break;
+ case SR_NUM_X15: regStr = "X15"; break;
+ case SR_NUM_XIP0: regStr = "XIP0"; break;
+ case SR_NUM_XIP1: regStr = "XIP1"; break;
+ case SR_NUM_LR: regStr = "LR"; break;
+#elif defined(_TARGET_AMD64_)
+ case SR_NUM_RAX: regStr = "RAX"; break;
+ case SR_NUM_RCX: regStr = "RCX"; break;
+ case SR_NUM_RDX: regStr = "RDX"; break;
+ case SR_NUM_R8: regStr = "R8"; break;
+ case SR_NUM_R9: regStr = "R9"; break;
+ case SR_NUM_R10: regStr = "R10"; break;
+ case SR_NUM_R11: regStr = "R11"; break;
+#elif defined(_TARGET_X86_)
+ case SR_NUM_RAX: regStr = "EAX"; break;
+ case SR_NUM_RCX: regStr = "ECX"; break;
+ case SR_NUM_RDX: regStr = "EDX"; break;
#else
- const char* baseReg = (b & 0x04) ? "EBP" : "ESP";
+#error unknown architecture
#endif
- const char* sign = (b & 0x02) ? "-" : "+";
- if (b & 0x01)
- {
- mask = VarInt::ReadUnsigned(pCursor);
+ }
+ gcPrintf("%s%s%s ", regStr, interior, pinned);
+ count++;
+ }
+ }
}
-
- int c = 1;
- while (pInts != pCursor)
+ else
{
- gcPrintf("%02x ", *pInts++);
- c++;
- }
+ // case 6 - stack slot / stack slot set
+ gcPrintf("%02x ", b);
+ unsigned mask = 0;
+ PTR_UInt8 pInts = pCursor;
+ unsigned offset = VarInt::ReadUnsigned(pCursor);
+ const char* interior = (b & 0x10) ? "+" : "";
+ const char* pinned = (b & 0x08) ? "!" : "";
+ const char* baseReg = (b & 0x04) ? GetFramePointerRegister() : GetStackPointerRegister();
+ const char* sign = (b & 0x02) ? "-" : "+";
+ if (b & 0x01)
+ {
+ mask = VarInt::ReadUnsigned(pCursor);
+ }
- for (; c < 4; c++)
- {
- gcPrintf(" ");
- }
+ int c = 1;
+ while (pInts != pCursor)
+ {
+ gcPrintf("%02x ", *pInts++);
+ c++;
+ }
- gcPrintf("| 6 [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
- count++;
+ for (; c < 4; c++)
+ {
+ gcPrintf(" ");
+ }
- while (mask > 0)
- {
- offset += POINTER_SIZE;
- if (mask & 1)
+ gcPrintf("| 6 [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
+ count++;
+
+ while (mask > 0)
{
- if (!first)
- gcPrintf(" ");
+ offset += POINTER_SIZE;
+ if (mask & 1)
+ {
+ if (!first)
+ gcPrintf(" ");
- gcPrintf(" | [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
- count++;
+ gcPrintf(" | [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned);
+ count++;
+ }
+ mask >>= 1;
}
- mask >>= 1;
}
}
break;
@@ -411,13 +632,21 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin
//gcPrintf("\n");
}
-
-
-
size_t FASTCALL GCDump::DumpGCTable (PTR_UInt8 gcInfo,
Tables * pTables,
const GCInfoHeader& header)
{
+ PTR_UInt8 pCursor = gcInfo;
+
+ if (header.HasCommonVars())
+ {
+ UInt32 commonVarCount = VarInt::ReadUnsigned(pCursor);
+ for (UInt32 i = 0; i < commonVarCount; i++)
+ {
+ VarInt::SkipUnsigned(pCursor);
+ }
+ }
+
//
// Decode the method GC info
//
@@ -439,7 +668,6 @@ size_t FASTCALL GCDump::DumpGCTable (PTR_UInt8 gcInfo,
// 11111111 -- STRING TERMINATOR
//
- PTR_UInt8 pCursor = gcInfo;
UInt32 curOffset = 0;
for (;;)
diff --git a/src/Native/Runtime/inc/WellKnownMethodList.h b/src/Native/Runtime/inc/WellKnownMethodList.h
index 567764bbc..e4079e6ca 100644
--- a/src/Native/Runtime/inc/WellKnownMethodList.h
+++ b/src/Native/Runtime/inc/WellKnownMethodList.h
@@ -9,3 +9,5 @@ DEFINE_WELL_KNOWN_METHOD(AppendExceptionStackFrame)
DEFINE_WELL_KNOWN_METHOD(CheckStaticClassConstruction)
DEFINE_WELL_KNOWN_METHOD(InitializeFinalizerThread)
DEFINE_WELL_KNOWN_METHOD(OnFirstChanceException)
+DEFINE_WELL_KNOWN_METHOD(DebugFuncEvalHelper)
+DEFINE_WELL_KNOWN_METHOD(DebugFuncEvalAbortHelper) \ No newline at end of file
diff --git a/src/Native/Runtime/inc/WellKnownMethods.h b/src/Native/Runtime/inc/WellKnownMethods.h
index 18ca5b82d..9e9614719 100644
--- a/src/Native/Runtime/inc/WellKnownMethods.h
+++ b/src/Native/Runtime/inc/WellKnownMethods.h
@@ -22,6 +22,8 @@ enum WellKnownMethodIds
};
#undef DEFINE_WELL_KNOWN_METHOD
+#ifdef BINDER
+
// Define an array of well known method names which are indexed by the enums defined above.
#define DEFINE_WELL_KNOWN_METHOD(_name) #_name,
extern __declspec(selectany) const char * const g_rgWellKnownMethodNames[] =
@@ -30,4 +32,6 @@ extern __declspec(selectany) const char * const g_rgWellKnownMethodNames[] =
};
#undef DEFINE_WELL_KNOWN_METHOD
+#endif // BINDER
+
#endif // !__WELLKNOWNMETHODS_INCLUDED
diff --git a/src/Native/Runtime/inc/gcinfo.h b/src/Native/Runtime/inc/gcinfo.h
index eb0497861..803cb9f6b 100644
--- a/src/Native/Runtime/inc/gcinfo.h
+++ b/src/Native/Runtime/inc/gcinfo.h
@@ -128,7 +128,7 @@ enum RegMask
RBM_X5 = 0x00000020,
RBM_X6 = 0x00000040,
RBM_X7 = 0x00000080,
- RBM_X8 = 0x00000100, // ARM64TODO: ARM64 ABI: indirect result register
+ RBM_X8 = 0x00000100, // ARM64 ABI: indirect result register
RBM_X9 = 0x00000200,
RBM_X10 = 0x00000400,
RBM_X11 = 0x00000800,
@@ -535,15 +535,19 @@ private:
{
struct
{
+#if defined(_TARGET_ARM64_)
+ UInt8 FPLRAreOnTop : 1; // [0] 1: FP and LR are saved on top of locals, not at the bottom (see MdmSaveFPAndLRAtTopOfLocalsArea)
+ UInt8 reg1ReturnKind : 2; // [1:2] One of MRK_Returns{Scalar|Object|Byref} constants describing value returned in x1 if any
+ UInt8 hasGSCookie : 1; // [3] 1: frame uses GS cookie
+ UInt8 hasCommonVars : 1; // [4] 1: method has a list of "common vars"
+ // as an optimization for methods with many call sites and variables
+ UInt8 : 3; // [5:7] unused bits
+#else
UInt8 logStackAlignment : 4; // [0:3] binary logarithm of frame alignment (3..15) or 0
UInt8 hasGSCookie : 1; // [4] 1: frame uses GS cookie
UInt8 hasCommonVars : 1; // [5] 1: method has a list of "common vars"
// as an optimization for methods with many call sites and variables
-#if defined(_TARGET_ARM64_)
- UInt8 FPLRAreOnTop : 1; // [6] 1: FP and LR are saved on top of locals, not at the bottom (see MdmSaveFPAndLRAtTopOfLocalsArea)
- UInt8 extraDataUnused : 1; // [7] unused bits
-#else
- UInt8 extraDataUnused : 2; // [6:7] unused bits
+ UInt8 : 2; // [6:7] unused bits
#endif
#pragma warning(suppress:4201) // nameless struct
};
@@ -608,7 +612,28 @@ public:
MRK_ReturnsObject = 1,
MRK_ReturnsByref = 2,
MRK_ReturnsToNative = 3,
+
+#if defined(_TARGET_ARM64_)
+ // Cases for structs returned in two registers.
+ // Naming scheme: MRK_reg0Kind_reg1Kind.
+ // Encoding scheme: <two bits for reg1Kind> <two bits for reg0Kind>.
+ // We do not distinguish returning a scalar in reg1 and no return value in reg1,
+ // which means we can use MRK_ReturnsObject for MRK_Obj_Scalar, etc.
+ MRK_Scalar_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsScalar,
+ MRK_Obj_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsObject,
+ MRK_Byref_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsByref,
+ MRK_Scalar_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsScalar,
+ MRK_Obj_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsObject,
+ MRK_Byref_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsByref,
+
+ MRK_LastValid = MRK_Byref_Byref,
+ // Illegal or uninitialized value. Never written to the image.
+ MRK_Unknown = 0xff,
+#else
+ MRK_LastValid = MRK_ReturnsToNative,
+ // Illegal or uninitialized value. Never written to the image.
MRK_Unknown = 4,
+#endif
};
enum EncodingConstants
@@ -672,7 +697,15 @@ public:
else
{
ASSERT(sizeInBytes != 0);
- PokeFixedEpilogSize(sizeInBytes);
+#if defined (_TARGET_ARM64_)
+ // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size.
+ ASSERT((sizeInBytes & 3) == 0);
+ fixedEpilogSize = sizeInBytes >> 2;
+ ASSERT(fixedEpilogSize == sizeInBytes >> 2);
+#else
+ fixedEpilogSize = sizeInBytes;
+ ASSERT(fixedEpilogSize == sizeInBytes);
+#endif
}
}
@@ -686,9 +719,10 @@ public:
epilogCountSmall = count < EC_MaxEpilogCountSmall ? count : EC_MaxEpilogCountSmall;
}
+#if !defined(_TARGET_ARM64_)
void SetReturnKind(MethodReturnKind kind)
{
- ASSERT(kind < MRK_Unknown); // not enough bits to encode 'unknown'
+ ASSERT(kind <= MRK_ReturnsToNative); // not enough bits to encode 'unknown'
returnKind = kind;
}
@@ -705,6 +739,7 @@ public:
ASSERT(logStackAlignment == logByteAlignment);
paramPointerReg = RN_NONE;
}
+#endif // !defined(_TARGET_ARM64_)
#if defined(_TARGET_ARM64_)
void SetFPLROnTop(void)
@@ -931,7 +966,11 @@ public:
UInt32 GetFixedEpilogSize()
{
ASSERT(!HasVaryingEpilogSizes());
- return PeekFixedEpilogSize();
+#if defined (_TARGET_ARM64_)
+ return fixedEpilogSize << 2;
+#else
+ return fixedEpilogSize;
+#endif
}
UInt32 GetEpilogCount()
@@ -946,7 +985,11 @@ public:
MethodReturnKind GetReturnKind()
{
+#if defined(_TARGET_ARM64_)
+ return (MethodReturnKind)((reg1ReturnKind << 2) | returnKind);
+#else
return (MethodReturnKind)returnKind;
+#endif
}
bool ReturnsToNative()
@@ -954,7 +997,7 @@ public:
return (GetReturnKind() == MRK_ReturnsToNative);
}
- bool HasFramePointer()
+ bool HasFramePointer() const
{
return !!ebpFrame;
}
@@ -989,12 +1032,21 @@ public:
bool HasDynamicAlignment()
{
+#if defined(_TARGET_ARM64_)
+ return false;
+#else
return !!logStackAlignment;
+#endif
}
UInt32 GetDynamicAlignment()
{
+#if defined(_TARGET_ARM64_)
+ ASSERT(!"Not supported");
+ return 1;
+#else
return 1 << logStackAlignment;
+#endif
}
bool HasGSCookie()
@@ -1003,7 +1055,7 @@ public:
}
#if defined(_TARGET_ARM64_)
- bool AreFPLROnTop()
+ bool AreFPLROnTop() const
{
return FPLRAreOnTop;
}
@@ -1015,7 +1067,7 @@ public:
return gsCookieOffset * POINTER_SIZE;
}
- bool HasCommonVars()
+ bool HasCommonVars() const
{
return hasCommonVars;
}
@@ -1028,7 +1080,7 @@ public:
#ifdef _TARGET_AMD64_
static const UInt32 SKEW_FOR_OFFSET_FROM_SP = 0x10;
- int GetFramePointerOffset() // returned in bytes
+ int GetFramePointerOffset() const // returned in bytes
{
// traditional frames where FP points to the pushed FP have fp offset == 0
if (x64_framePtrOffset == 0)
@@ -1048,12 +1100,12 @@ public:
return offsetFromSP - preservedRegsSaveSize - GetFrameSize();
}
- bool IsFramePointerOffsetFromSP()
+ bool IsFramePointerOffsetFromSP() const
{
return x64_framePtrOffset != 0;
}
- int GetFramePointerOffsetFromSP()
+ int GetFramePointerOffsetFromSP() const
{
ASSERT(IsFramePointerOffsetFromSP());
int offsetFromSP;
@@ -1095,7 +1147,7 @@ public:
}
#endif
- int GetFrameSize()
+ int GetFrameSize() const
{
return frameSize * POINTER_SIZE;
}
@@ -1126,7 +1178,7 @@ public:
return (CalleeSavedRegMask) calleeSavedRegMask;
}
- bool IsRegSaved(CalleeSavedRegMask reg)
+ bool IsRegSaved(CalleeSavedRegMask reg) const
{
return (0 != (calleeSavedRegMask & reg));
}
@@ -1449,7 +1501,7 @@ public:
// the per-method epilog table, so at least we're consistent with what is encoded.
UInt8 mainEpilogAtEnd = epilogAtEnd;
UInt16 mainEpilogCount = epilogCount;
- UInt16 mainFixedEpilogSize = (UInt16) PeekFixedEpilogSize();
+ UInt16 mainFixedEpilogSize = fixedEpilogSize; // Either in bytes or in instructions
UInt8 mainHasCommonVars = hasCommonVars;
// -------
@@ -1589,15 +1641,24 @@ public:
#ifdef RHDUMP
char const * GetBoolStr(bool val) { return val ? " true" : "false"; }
- char const * GetRetKindStr(int k)
+
+ char const * GetRetKindStr(MethodReturnKind kind)
{
- switch (k)
+ switch (kind)
{
case MRK_ReturnsScalar: return "scalar";
case MRK_ReturnsObject: return "object";
- case MRK_ReturnsByref: return " byref";
+ case MRK_ReturnsByref: return "byref";
case MRK_ReturnsToNative: return "native";
- default: return "unknwn";
+#if defined(_TARGET_ARM64_)
+ case MRK_Scalar_Obj: return "{scalar, object}";
+ case MRK_Scalar_Byref: return "{scalar, byref}";
+ case MRK_Obj_Obj: return "{object, object}";
+ case MRK_Obj_Byref: return "{object, byref}";
+ case MRK_Byref_Obj: return "{byref, object}";
+ case MRK_Byref_Byref: return "{byref, byref}";
+#endif // defined(_TARGET_ARM64_)
+ default: return "unknown";
}
}
@@ -1739,10 +1800,10 @@ public:
void Dump()
{
- printf(" | prologSize: %02X"" | epilogSize: %02X"" | epilogCount: %02X"" | epilogAtEnd: %s\n",
- GetPrologSize(), PeekFixedEpilogSize(), epilogCount, GetBoolStr(epilogAtEnd));
- printf(" | frameSize: %04X"" | ebpFrame: %s"" | hasFunclets: %s"" | returnKind: %s\n",
- GetFrameSize(), GetBoolStr(ebpFrame), GetBoolStr(hasFunclets), GetRetKindStr(returnKind));
+ printf(" | prologSize: %02X"" | epilogSize: %02X"" | epilogCount: %02X"" | epilogAtEnd: %s\n",
+ GetPrologSize(), HasVaryingEpilogSizes() ? 0 : GetFixedEpilogSize(), epilogCount, GetBoolStr(epilogAtEnd));
+ printf(" | frameSize: %04X"" | ebpFrame: %s"" | hasFunclets: %s"" | returnKind: %s\n",
+ GetFrameSize(), GetBoolStr(ebpFrame), GetBoolStr(hasFunclets), GetRetKindStr(GetReturnKind()));
printf(" | regMask: %04X" " {", calleeSavedRegMask);
PrintCalleeSavedRegs(calleeSavedRegMask);
printf(" }\n");
diff --git a/src/Native/Runtime/inc/rhbinder.h b/src/Native/Runtime/inc/rhbinder.h
index 1dee73202..01fbbfcf6 100644
--- a/src/Native/Runtime/inc/rhbinder.h
+++ b/src/Native/Runtime/inc/rhbinder.h
@@ -62,7 +62,8 @@ struct ModuleHeader
// breaking changes
DELTA_SHORTCUT_TABLE_SIZE = 16,
MAX_REGIONS = 8, // Max number of regions described by the Regions array
- MAX_WELL_KNOWN_METHODS = 8, // Max number of methods described by the WellKnownMethods array
+ MAX_WELL_KNOWN_METHODS = 8, // Max number of methods described by the WellKnownMethods array
+ MAX_EXTRA_WELL_KNOWN_METHODS= 8, // Max number of methods described by the ExtraWellKnownMethods array
NULL_RRA = 0xffffffff, // NULL value for region relative addresses (0 is often a
// legal RRA)
};
@@ -141,6 +142,9 @@ struct ModuleHeader
UInt32 RraColdToHotMappingInfo;
+ UInt32 ExtraWellKnownMethods[MAX_EXTRA_WELL_KNOWN_METHODS]; // Array of methods with well known semantics defined
+ // in this module
+
// Macro to generate an inline accessor for RRA-based fields.
#ifdef RHDUMP
#define DEFINE_GET_ACCESSOR(_field, _region)\
@@ -202,10 +206,19 @@ struct ModuleHeader
#ifndef RHDUMP
// Macro to generate an inline accessor for well known methods (these are all TEXT-based RRAs since they
// point to code).
-#define DEFINE_WELL_KNOWN_METHOD(_name) \
- inline PTR_VOID Get_##_name() \
- { \
- return WellKnownMethods[WKM_##_name] == NULL_RRA ? NULL : RegionPtr[TEXT_REGION] + WellKnownMethods[WKM_##_name]; \
+#define DEFINE_WELL_KNOWN_METHOD(_name) \
+ inline PTR_VOID Get_##_name() \
+ { \
+ unsigned int index = (unsigned int)WKM_##_name; \
+ if (index >= MAX_WELL_KNOWN_METHODS) \
+ { \
+ index = index - MAX_WELL_KNOWN_METHODS; \
+ return ExtraWellKnownMethods[index] == NULL_RRA ? NULL : RegionPtr[TEXT_REGION] + ExtraWellKnownMethods[index]; \
+ } \
+ else \
+ { \
+ return WellKnownMethods[index] == NULL_RRA ? NULL : RegionPtr[TEXT_REGION] + WellKnownMethods[index]; \
+ } \
}
#include "WellKnownMethodList.h"
#undef DEFINE_WELL_KNOWN_METHOD
@@ -649,14 +662,38 @@ enum PInvokeTransitionFrameFlags : UInt64
// a return address pointing into the hijacked method and that method's
// lr register, which may hold a gc pointer
- // Other flags
- PTFF_X0_IS_GCREF = 0x0000000100000000, // used by hijack handler to report return value of hijacked method
- PTFF_X0_IS_BYREF = 0x0000000200000000, // used by hijack handler to report return value of hijacked method
- PTFF_X1_IS_GCREF = 0x0000000400000000, // used by hijack handler to report return value of hijacked method
- PTFF_X1_IS_BYREF = 0x0000000800000000, // used by hijack handler to report return value of hijacked method
+ // used by hijack handler to report return value of hijacked method
+ PTFF_X0_IS_GCREF = 0x0000000100000000,
+ PTFF_X0_IS_BYREF = 0x0000000200000000,
+ PTFF_X1_IS_GCREF = 0x0000000400000000,
+ PTFF_X1_IS_BYREF = 0x0000000800000000,
PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition
};
+
+// TODO: Consider moving the PInvokeTransitionFrameFlags definition to a separate file to simplify header dependencies
+#ifdef ICODEMANAGER_INCLUDED
+// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back
+C_ASSERT(PTFF_X0_IS_GCREF == ((UInt64)GCRK_Object << 32));
+C_ASSERT(PTFF_X0_IS_BYREF == ((UInt64)GCRK_Byref << 32));
+C_ASSERT(PTFF_X1_IS_GCREF == ((UInt64)GCRK_Scalar_Obj << 32));
+C_ASSERT(PTFF_X1_IS_BYREF == ((UInt64)GCRK_Scalar_Byref << 32));
+
+inline UInt64 ReturnKindToTransitionFrameFlags(GCRefKind returnKind)
+{
+ if (returnKind == GCRK_Scalar)
+ return 0;
+
+ return PTFF_SAVE_X0 | PTFF_SAVE_X1 | ((UInt64)returnKind << 32);
+}
+
+inline GCRefKind TransitionFrameFlagsToReturnKind(UInt64 transFrameFlags)
+{
+ GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_X0_IS_GCREF | PTFF_X0_IS_BYREF | PTFF_X1_IS_GCREF | PTFF_X1_IS_BYREF)) >> 32);
+ ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_X0) && (transFrameFlags & PTFF_SAVE_X1)));
+ return returnKind;
+}
+#endif // ICODEMANAGER_INCLUDED
#else // _TARGET_ARM_
enum PInvokeTransitionFrameFlags
{
diff --git a/src/Native/Runtime/module.cpp b/src/Native/Runtime/module.cpp
index 6cfa13f95..067f361b8 100644
--- a/src/Native/Runtime/module.cpp
+++ b/src/Native/Runtime/module.cpp
@@ -824,6 +824,12 @@ void * Module::GetClasslibFunction(ClasslibFunctionId functionId)
case ClasslibFunctionId::OnFirstChanceException:
pMethod = m_pModuleHeader->Get_OnFirstChanceException();
break;
+ case ClasslibFunctionId::DebugFuncEvalHelper:
+ pMethod = m_pModuleHeader->Get_DebugFuncEvalHelper();
+ break;
+ case ClasslibFunctionId::DebugFuncEvalAbortHelper:
+ pMethod = m_pModuleHeader->Get_DebugFuncEvalAbortHelper();
+ break;
default:
pMethod = NULL;
break;
@@ -1165,7 +1171,7 @@ void Module::DoCustomImports(ModuleHeader * pModuleHeader)
// obtain address of indirection cell pointing to the EAT for the exporting module
UInt32 **ptrPtrEAT = (UInt32 **)(thisBaseAddress + customImportTable[i].RvaEATAddr);
- // obtain the EAT by derefencing
+ // obtain the EAT by dereferencing
UInt32 *ptrEAT = *ptrPtrEAT;
// obtain the exporting module
diff --git a/src/Native/Runtime/stressLog.cpp b/src/Native/Runtime/stressLog.cpp
index 5e6278091..a0b097c89 100644
--- a/src/Native/Runtime/stressLog.cpp
+++ b/src/Native/Runtime/stressLog.cpp
@@ -71,7 +71,7 @@ unsigned __int64 getTimeStamp() {
#endif // _X86_ else
/*********************************************************************************/
-/* Get the the frequency cooresponding to 'getTimeStamp'. For non-x86
+/* Get the the frequency corresponding to 'getTimeStamp'. For non-x86
architectures, this is just the performance counter frequency.
*/
unsigned __int64 getTickFrequency()
diff --git a/src/Native/Runtime/thread.cpp b/src/Native/Runtime/thread.cpp
index 059ec5abe..994c21dad 100644
--- a/src/Native/Runtime/thread.cpp
+++ b/src/Native/Runtime/thread.cpp
@@ -450,12 +450,27 @@ bool Thread::GcScanRoots(GcScanRootsCallbackFunc * pfnEnumCallback, void * token
void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, StackFrameIterator & frameIterator)
{
- PTR_RtuObjectRef pHijackedReturnValue = NULL;
- GCRefKind ReturnValueKind = GCRK_Unknown;
+ PTR_RtuObjectRef pHijackedReturnValue = NULL;
+ GCRefKind returnValueKind = GCRK_Unknown;
- if (frameIterator.GetHijackedReturnValueLocation(&pHijackedReturnValue, &ReturnValueKind))
+ if (frameIterator.GetHijackedReturnValueLocation(&pHijackedReturnValue, &returnValueKind))
{
- RedhawkGCInterface::EnumGcRef(pHijackedReturnValue, ReturnValueKind, pfnEnumCallback, pvCallbackData);
+#ifdef _TARGET_ARM64_
+ GCRefKind reg0Kind = ExtractReg0ReturnKind(returnValueKind);
+ GCRefKind reg1Kind = ExtractReg1ReturnKind(returnValueKind);
+
+ // X0 and X1 are saved next to each other in this order
+ if (reg0Kind != GCRK_Scalar)
+ {
+ RedhawkGCInterface::EnumGcRef(pHijackedReturnValue, reg0Kind, pfnEnumCallback, pvCallbackData);
+ }
+ if (reg1Kind != GCRK_Scalar)
+ {
+ RedhawkGCInterface::EnumGcRef(pHijackedReturnValue + 1, reg1Kind, pfnEnumCallback, pvCallbackData);
+ }
+#else
+ RedhawkGCInterface::EnumGcRef(pHijackedReturnValue, returnValueKind, pfnEnumCallback, pvCallbackData);
+#endif
}
#ifndef DACCESS_COMPILE
@@ -507,7 +522,7 @@ void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, St
// interface invocation slow paths for instance. Since the original managed call may have passed GC
// references which are unreported by any managed method on the stack at the time of the GC we
// identify (again conservatively) the range of the stack that might contain these references and
- // report everything. Since it should be a very rare occurance indeed that we actually have to do
+ // report everything. Since it should be a very rare occurrence indeed that we actually have to do
// this this, it's considered a better trade-off than storing signature metadata for every potential
// callsite of the type described above.
if (frameIterator.HasStackRangeToReportConservatively())
@@ -544,40 +559,58 @@ void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, St
#ifndef DACCESS_COMPILE
+#ifndef _TARGET_ARM64_
EXTERN_C void FASTCALL RhpGcProbeHijackScalar();
EXTERN_C void FASTCALL RhpGcProbeHijackObject();
EXTERN_C void FASTCALL RhpGcProbeHijackByref();
-static void* NormalHijackTargets[3] =
+static void* NormalHijackTargets[3] =
{
reinterpret_cast<void*>(RhpGcProbeHijackScalar), // GCRK_Scalar = 0,
- reinterpret_cast<void*>(RhpGcProbeHijackObject), // GCRK_Object = 1,
+ reinterpret_cast<void*>(RhpGcProbeHijackObject), // GCRK_Object = 1,
reinterpret_cast<void*>(RhpGcProbeHijackByref) // GCRK_Byref = 2,
};
+#else // _TARGET_ARM64_
+EXTERN_C void FASTCALL RhpGcProbeHijack();
+
+static void* NormalHijackTargets[1] =
+{
+ reinterpret_cast<void*>(RhpGcProbeHijack)
+};
+#endif // _TARGET_ARM64_
#ifdef FEATURE_GC_STRESS
+#ifndef _TARGET_ARM64_
EXTERN_C void FASTCALL RhpGcStressHijackScalar();
EXTERN_C void FASTCALL RhpGcStressHijackObject();
EXTERN_C void FASTCALL RhpGcStressHijackByref();
-static void* GcStressHijackTargets[3] =
-{
+static void* GcStressHijackTargets[3] =
+{
reinterpret_cast<void*>(RhpGcStressHijackScalar), // GCRK_Scalar = 0,
- reinterpret_cast<void*>(RhpGcStressHijackObject), // GCRK_Object = 1,
+ reinterpret_cast<void*>(RhpGcStressHijackObject), // GCRK_Object = 1,
reinterpret_cast<void*>(RhpGcStressHijackByref) // GCRK_Byref = 2,
};
+#else // _TARGET_ARM64_
+EXTERN_C void FASTCALL RhpGcStressHijack();
+
+static void* GcStressHijackTargets[1] =
+{
+ reinterpret_cast<void*>(RhpGcStressHijack)
+};
+#endif // _TARGET_ARM64_
#endif // FEATURE_GC_STRESS
// static
bool Thread::IsHijackTarget(void * address)
{
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < COUNTOF(NormalHijackTargets); i++)
{
if (NormalHijackTargets[i] == address)
return true;
}
#ifdef FEATURE_GC_STRESS
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < COUNTOF(GcStressHijackTargets); i++)
{
if (GcStressHijackTargets[i] == address)
return true;
@@ -683,7 +716,7 @@ void Thread::HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx)
// 2) from another thread to place a return hijack onto this thread's stack. In this case the target
// thread is OS suspended someplace in managed code. The only constraint on the suspension is that the
// stack be crawlable enough to yield the location of the return address.
-bool Thread::InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void* HijackTargets[3])
+bool Thread::InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void * pvHijackTargets[])
{
bool fSuccess = false;
@@ -716,10 +749,14 @@ bool Thread::InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void* HijackTarge
m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation;
m_pvHijackedReturnAddress = pvRetAddr;
- void* pvHijackTarget = HijackTargets[retValueKind];
+#ifdef _TARGET_ARM64_
+ m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags(retValueKind);
+ *ppvRetAddrLocation = pvHijackTargets[0];
+#else
+ void* pvHijackTarget = pvHijackTargets[retValueKind];
ASSERT_MSG(IsHijackTarget(pvHijackTarget), "unexpected method used as hijack target");
*ppvRetAddrLocation = pvHijackTarget;
-
+#endif
fSuccess = true;
}
}
@@ -768,6 +805,9 @@ void Thread::UnhijackWorker()
// Clear the hijack state.
m_ppvHijackedReturnAddressLocation = NULL;
m_pvHijackedReturnAddress = NULL;
+#ifdef _TARGET_ARM64_
+ m_uHijackedReturnValueFlags = 0;
+#endif
}
#if _DEBUG
diff --git a/src/Native/Runtime/thread.h b/src/Native/Runtime/thread.h
index 930114ba4..c3934c823 100644
--- a/src/Native/Runtime/thread.h
+++ b/src/Native/Runtime/thread.h
@@ -14,19 +14,19 @@ class Thread;
// runtime build.
#define KEEP_THREAD_LAYOUT_CONSTANT
-#if defined(_X86_) || defined(_ARM_) || defined(_WASM_)
+#ifndef BIT64
# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT)
# define SIZEOF_ALLOC_CONTEXT 40
-# else // FEATURE_SVR_GC
+# else
# define SIZEOF_ALLOC_CONTEXT 28
-# endif // FEATURE_SVR_GC
-#elif defined(_AMD64_) || defined(_ARM64_)
+# endif
+#else // BIT64
# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT)
# define SIZEOF_ALLOC_CONTEXT 56
-# else // FEATURE_SVR_GC
+# else
# define SIZEOF_ALLOC_CONTEXT 40
-# endif // FEATURE_SVR_GC
-#endif // _AMD64_
+# endif
+#endif // BIT64
#define TOP_OF_STACK_MARKER ((PTR_VOID)(UIntNative)(IntNative)-1)
@@ -60,8 +60,6 @@ struct ExInfo
volatile void* m_notifyDebuggerSP;
};
-
-
struct ThreadBuffer
{
UInt8 m_rgbAllocContextBuffer[SIZEOF_ALLOC_CONTEXT];
@@ -77,6 +75,9 @@ struct ThreadBuffer
HANDLE m_hPalThread; // WARNING: this may legitimately be INVALID_HANDLE_VALUE
void ** m_ppvHijackedReturnAddressLocation;
void * m_pvHijackedReturnAddress;
+#ifdef BIT64
+ UIntNative m_uHijackedReturnValueFlags; // used on ARM64 only; however, ARM64 and AMD64 share field offsets
+#endif // BIT64
PTR_ExInfo m_pExInfoStackHead;
Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort
PTR_VOID m_pStackLow;
@@ -136,7 +137,7 @@ private:
bool IsStateSet(ThreadStateFlags flags);
static UInt32_BOOL HijackCallback(HANDLE hThread, PAL_LIMITED_CONTEXT* pThreadContext, void* pCallbackContext);
- bool InternalHijack(PAL_LIMITED_CONTEXT * pCtx, void* HijackTargets[3]);
+ bool InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void * pvHijackTargets[]);
bool CacheTransitionFrameForSuspend();
void ResetCachedTransitionFrame();
@@ -161,7 +162,7 @@ public:
bool IsInitialized();
- gc_alloc_context * GetAllocContext(); // @TODO: I would prefer to not expose this in this way
+ gc_alloc_context * GetAllocContext(); // @TODO: I would prefer to not expose this in this way
#ifndef DACCESS_COMPILE
UInt64 GetPalThreadIdForLogging();
@@ -176,7 +177,7 @@ public:
bool Hijack();
void Unhijack();
#ifdef FEATURE_GC_STRESS
- static void HijackForGcStress(PAL_LIMITED_CONTEXT * pCtx);
+ static void HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx);
#endif // FEATURE_GC_STRESS
bool IsHijacked();
void * GetHijackedReturnAddress();
diff --git a/src/Native/Runtime/threadstore.cpp b/src/Native/Runtime/threadstore.cpp
index d359c681d..f3a019ae3 100644
--- a/src/Native/Runtime/threadstore.cpp
+++ b/src/Native/Runtime/threadstore.cpp
@@ -410,11 +410,7 @@ EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread =
INVALID_HANDLE_VALUE, // m_hPalThread
0, // m_ppvHijackedReturnAddressLocation
0, // m_pvHijackedReturnAddress
- 0, // m_pExInfoStackHead
- 0, // m_pStackLow
- 0, // m_pStackHigh
- 0, // m_pTEB
- 0, // m_uPalThreadIdForLogging
+ 0, // all other fields are initialized by zeroes
};
#endif // !DACCESS_COMPILE
diff --git a/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/src/Native/Runtime/unix/PalRedhawkUnix.cpp
index 6d151657e..0807d8004 100644
--- a/src/Native/Runtime/unix/PalRedhawkUnix.cpp
+++ b/src/Native/Runtime/unix/PalRedhawkUnix.cpp
@@ -308,7 +308,7 @@ public:
TimeSpecAdd(&endTime, milliseconds);
}
#else
-#error Don't know how to perfom timed wait on this platform
+#error Don't know how to perform timed wait on this platform
#endif
int st = 0;
@@ -1044,8 +1044,13 @@ extern "C" void LeaveCriticalSection(CRITICAL_SECTION * lpCriticalSection)
extern "C" UInt32_BOOL IsDebuggerPresent()
{
+#ifdef _WASM_
+ // For now always true since the browser will handle it in case of WASM.
+ return UInt32_TRUE;
+#else
// UNIXTODO: Implement this function
return UInt32_FALSE;
+#endif
}
extern "C" void TerminateProcess(HANDLE arg1, UInt32 arg2)
diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp
index cf88a5994..18fff0ccb 100644
--- a/src/Native/Runtime/unix/UnixContext.cpp
+++ b/src/Native/Runtime/unix/UnixContext.cpp
@@ -591,7 +591,12 @@ bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* ls
assert((procInfo.start_ip <= controlPC) && (controlPC < procInfo.end_ip));
+#if defined(_ARM_)
+ // libunwind fills by reference not by value for ARM
+ *lsda = *((UIntNative *)procInfo.lsda);
+#else
*lsda = procInfo.lsda;
+#endif
*startAddress = procInfo.start_ip;
return true;
diff --git a/src/Native/Runtime/unix/UnwindHelpers.cpp b/src/Native/Runtime/unix/UnwindHelpers.cpp
index ed5100bd8..c77de8200 100644
--- a/src/Native/Runtime/unix/UnwindHelpers.cpp
+++ b/src/Native/Runtime/unix/UnwindHelpers.cpp
@@ -4,6 +4,7 @@
#include "common.h"
#include "daccess.h"
+#include "rhassert.h"
#define UNW_STEP_SUCCESS 1
#define UNW_STEP_END 0
@@ -20,12 +21,23 @@
#include <src/config.h>
#include <src/Registers.hpp>
#include <src/AddressSpace.hpp>
+#if defined(_TARGET_ARM_)
+#include <src/libunwind_ext.h>
+#endif
#include <src/UnwindCursor.hpp>
+#if defined(_TARGET_AMD64_)
using libunwind::Registers_x86_64;
+#elif defined(_TARGET_ARM_)
+using libunwind::Registers_arm;
+#else
+#error "Unwinding is not implemented for this architecture yet."
+#endif
using libunwind::LocalAddressSpace;
using libunwind::EHHeaderParser;
+#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
using libunwind::DwarfInstructions;
+#endif
using libunwind::UnwindInfoSections;
LocalAddressSpace _addressSpace;
@@ -43,6 +55,7 @@ struct dyld_unwind_sections
#else // __APPLE__
+#if defined(_TARGET_AMD64_)
// Passed to the callback function called by dl_iterate_phdr
struct dl_iterate_cb_data
{
@@ -108,6 +121,7 @@ static int LocateSectionsCallback(struct dl_phdr_info *info, size_t size, void *
return 0;
}
+#endif
#endif // __APPLE__
@@ -289,6 +303,7 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs
#error "Unwinding is not implemented for this architecture yet."
#endif
+#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, 0 /* fdeSectionOffsetHint */);
if (!retVal)
{
@@ -307,6 +322,9 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs
}
regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR));
+#else
+ PORTABILITY_ASSERT("DoTheStep");
+#endif
return true;
}
@@ -333,8 +351,12 @@ UnwindInfoSections LocateUnwindSections(uintptr_t pc)
}
#else // __APPLE__
+#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
dl_iterate_cb_data cb_data = {&uwInfoSections, pc };
dl_iterate_phdr(LocateSectionsCallback, &cb_data);
+#else
+ PORTABILITY_ASSERT("LocateUnwindSections");
+#endif
#endif
@@ -346,10 +368,15 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs)
uintptr_t pc = regs->GetIP();
UnwindInfoSections uwInfoSections = LocateUnwindSections(pc);
+
+#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
if (uwInfoSections.dwarf_section == NULL)
{
return false;
}
+#else
+ PORTABILITY_ASSERT("StepFrame");
+#endif
return DoTheStep(pc, uwInfoSections, regs);
}
diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp
index e02412904..22a37bb92 100644
--- a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp
+++ b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp
@@ -528,7 +528,7 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
p += sizeof(int32_t);
- // Decode the GC info for the current method to detemine its return type
+ // Decode the GC info for the current method to determine its return type
GcInfoDecoder decoder(
GCInfoToken(p),
GcInfoDecoderFlags(DECODE_RETURN_KIND),
diff --git a/src/Native/Runtime/windows/PalRedhawkMinWin.cpp b/src/Native/Runtime/windows/PalRedhawkMinWin.cpp
index 366afce8f..95095e1db 100644
--- a/src/Native/Runtime/windows/PalRedhawkMinWin.cpp
+++ b/src/Native/Runtime/windows/PalRedhawkMinWin.cpp
@@ -1056,8 +1056,7 @@ UInt32 CountBits(size_t bfBitfield)
// 'answers' between the current implementation and the CLR implementation.
//
//#define TRACE_CACHE_TOPOLOGY
-#if defined(_DEBUG) && !defined(_ARM64_)
-// ARM64TODO: restore
+#ifdef _DEBUG
void DumpCacheTopology(_In_reads_(cRecords) SYSTEM_LOGICAL_PROCESSOR_INFORMATION * pProcInfos, UInt32 cRecords)
{
printf("----------------\n");
@@ -1101,6 +1100,7 @@ void DumpCacheTopology(_In_reads_(cRecords) SYSTEM_LOGICAL_PROCESSOR_INFORMATION
}
printf("----------------\n");
}
+
void DumpCacheTopologyResults(UInt32 maxCpuId, CpuVendor cpuVendor, _In_reads_(cRecords) SYSTEM_LOGICAL_PROCESSOR_INFORMATION * pProcInfos, UInt32 cRecords)
{
DumpCacheTopology(pProcInfos, cRecords);
@@ -1109,7 +1109,7 @@ void DumpCacheTopologyResults(UInt32 maxCpuId, CpuVendor cpuVendor, _In_reads_(c
printf(" g_cbLargestOnDieCache: 0x%08zx 0x%08zx :CLR_LargestOnDieCache(TRUE)\n", g_cbLargestOnDieCache, CLR_GetLargestOnDieCacheSize(TRUE, pProcInfos, cRecords));
printf("g_cbLargestOnDieCacheAdjusted: 0x%08zx 0x%08zx :CLR_LargestOnDieCache(FALSE)\n", g_cbLargestOnDieCacheAdjusted, CLR_GetLargestOnDieCacheSize(FALSE, pProcInfos, cRecords));
}
-#endif // defined(_DEBUG) && !defined(_ARM64_)
+#endif // _DEBUG
// Method used to initialize the above values.
bool PalQueryProcessorTopology()
@@ -1282,21 +1282,18 @@ bool PalQueryProcessorTopology()
g_cbLargestOnDieCache = cbCache;
g_cbLargestOnDieCacheAdjusted = cbCacheAdjusted;
-#if defined(_DEBUG)
-#if defined(TRACE_CACHE_TOPOLOGY) && !defined(_ARM64_)
-// ARM64TODO: restore
+#ifdef _DEBUG
+#ifdef TRACE_CACHE_TOPOLOGY
DumpCacheTopologyResults(maxCpuId, cpuVendor, pProcInfos, cRecords);
-#endif // defined(TRACE_CACHE_TOPOLOGY) && !defined(_ARM64_)
+#endif
if ((CLR_GetLargestOnDieCacheSize(TRUE, pProcInfos, cRecords) != g_cbLargestOnDieCache) ||
(CLR_GetLargestOnDieCacheSize(FALSE, pProcInfos, cRecords) != g_cbLargestOnDieCacheAdjusted) ||
(CLR_GetLogicalCpuCount(pProcInfos, cRecords) != g_cLogicalCpus))
{
-#if !defined(_ARM64_)
DumpCacheTopologyResults(maxCpuId, cpuVendor, pProcInfos, cRecords);
-#endif
assert(!"QueryProcessorTopology doesn't match CLR's results. See stdout for more info.");
}
-#endif
+#endif // _DEBUG
}
if (pProcInfos)
diff --git a/src/Native/System.Private.CoreLib.Native/pal_threading.cpp b/src/Native/System.Private.CoreLib.Native/pal_threading.cpp
index 3f9f37276..e3c2dd797 100644
--- a/src/Native/System.Private.CoreLib.Native/pal_threading.cpp
+++ b/src/Native/System.Private.CoreLib.Native/pal_threading.cpp
@@ -28,7 +28,7 @@ extern "C" void CoreLibNative_LowLevelMutex_Release(LowLevelMutex *mutex)
// LowLevelMonitor
#if !(HAVE_MACH_ABSOLUTE_TIME || HAVE_PTHREAD_CONDATTR_SETCLOCK && HAVE_CLOCK_MONOTONIC)
-#error Don't know how to perfom timed wait on this platform
+#error Don't know how to perform timed wait on this platform
#endif
LowLevelMonitor::LowLevelMonitor(bool abortOnFailure, bool *successRef) : LowLevelMutex(abortOnFailure, successRef)
diff --git a/src/Native/gc/gc.cpp b/src/Native/gc/gc.cpp
index 649f2cdd8..5c673c0be 100644
--- a/src/Native/gc/gc.cpp
+++ b/src/Native/gc/gc.cpp
@@ -3065,7 +3065,7 @@ gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
}
// DTREVIEW: Right now we only estimate gen2 fragmentation.
-// on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
+// on 64-bit though we should consider gen1 or even gen0 fragmentation as
// well
inline BOOL
gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
@@ -3620,7 +3620,7 @@ gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
- dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
+ dprintf (2, ("checking obj %Ix, index is %Id, entry: boundary: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
o, index, (entry->boundary + 1),
(uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
(uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
@@ -3689,7 +3689,7 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o)
size_t index = (size_t)o / gc_heap::min_segment_size;
seg_mapping* entry = &seg_mapping_table[index];
- dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
+ dprintf (2, ("checking obj %Ix, index is %Id, entry: boundary: %Ix, seg0: %Ix, seg1: %Ix",
o, index, (entry->boundary + 1),
(uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
@@ -3701,7 +3701,7 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o)
if (seg)
{
- // Can't assert this when it's callled by everyone (it's true when it's called by mark cards).
+ // Can't assert this when it's called by everyone (it's true when it's called by mark cards).
//assert (in_range_for_segment (o, seg));
if (in_range_for_segment (o, seg))
{
@@ -4519,7 +4519,7 @@ gc_heap::soh_get_segment_to_expand()
dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
// If we return 0 here, the allocator will think since we are short on end
- // of seg we neeed to trigger a full compacting GC. So if sustained low latency
+ // of seg we need to trigger a full compacting GC. So if sustained low latency
// is set we should acquire a new seg instead, that way we wouldn't be short.
// The real solution, of course, is to actually implement seg reuse in gen1.
if (settings.pause_mode != pause_sustained_low_latency)
@@ -4893,7 +4893,7 @@ extern "C" uint64_t __rdtsc();
#elif defined(_TARGET_WASM_)
static ptrdiff_t get_cycle_count()
{
- // @WASMTODO: cycle counter is not exposed to user mode by WebAsssembly. For now (until we can show this
+ // @WASMTODO: cycle counter is not exposed to user mode by WebAssembly. For now (until we can show this
// makes a difference on the configurations on which we'll run) just return 0. This will result in
// all buffer access times being reported as equal in access_time().
return 0;
@@ -5420,7 +5420,7 @@ public:
// Supposedly Pinned objects cannot have references but we are seeing some from pinvoke
// frames. Also if it's an artificially pinned plug created by us, it can certainly
// have references.
- // We know these cases will be rare so we can optimize this to be only allocated on decommand.
+ // We know these cases will be rare so we can optimize this to be only allocated on demand.
gap_reloc_pair saved_post_plug_reloc;
// We need to calculate this after we are done with plan phase and before compact
@@ -6217,7 +6217,7 @@ void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, gener
size_t gc_heap::deque_pinned_plug ()
{
- dprintf (3, ("dequed: %Id", mark_stack_bos));
+ dprintf (3, ("deque: %Id", mark_stack_bos));
size_t m = mark_stack_bos;
mark_stack_bos++;
return m;
@@ -7065,7 +7065,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
{
{
- //modify the higest address so the span covered
+ //modify the highest address so the span covered
//is twice the previous one.
uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
// On non-Windows systems, we get only an approximate value that can possibly be
@@ -8281,7 +8281,7 @@ void gc_heap::combine_mark_lists()
assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
if (end_of_list > &g_mark_list[0])
_sort (&g_mark_list[0], end_of_list, 0);
- //adjust the mark_list to the begining of the resulting mark list.
+ //adjust the mark_list to the beginning of the resulting mark list.
for (int i = 0; i < n_heaps; i++)
{
g_heaps [i]->mark_list = g_mark_list;
@@ -8292,7 +8292,7 @@ void gc_heap::combine_mark_lists()
else
{
uint8_t** end_of_list = g_mark_list;
- //adjust the mark_list to the begining of the resulting mark list.
+ //adjust the mark_list to the beginning of the resulting mark list.
//put the index beyond the end to turn off mark list processing
for (int i = 0; i < n_heaps; i++)
{
@@ -8403,7 +8403,7 @@ class seg_free_spaces
struct free_space_bucket
{
seg_free_space* free_space;
- ptrdiff_t count_add; // Assigned when we first contruct the array.
+ ptrdiff_t count_add; // Assigned when we first construct the array.
ptrdiff_t count_fit; // How many items left when we are fitting plugs.
};
@@ -8691,7 +8691,7 @@ public:
// BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
_ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
#endif // FEATURE_STRUCTALIGN
- // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the
+ // TODO: this is also not large alignment ready. We would need to consider alignment when choosing the
// the bucket.
size_t plug_size_to_fit = plug_size;
@@ -10441,7 +10441,7 @@ gc_heap::init_gc_heap (int h_number)
{
#ifndef INTERIOR_POINTERS
//set the brick_table for large objects
- //but default value is clearded
+ //but default value is cleared
//clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
// (uint8_t*)heap_segment_reserved (lseg));
@@ -11566,7 +11566,7 @@ check_other_factors:
dprintf (2, ("FGN: we estimate gen%d will be collected", n));
#ifdef BACKGROUND_GC
- // When background GC is enabled it decreases the accurancy of our predictability -
+ // When background GC is enabled it decreases the accuracy of our predictability -
// by the time the GC happens, we may not be under BGC anymore. If we try to
// predict often enough it should be ok.
if ((n == max_generation) &&
@@ -14703,7 +14703,7 @@ int gc_heap::generation_to_condemn (int n_initial,
}
}
- //figure out which ephemeral generation is too fragramented
+ //figure out which ephemeral generation is too fragmented
temp_gen = n;
for (i = n+1; i < max_generation; i++)
{
@@ -17404,7 +17404,7 @@ void gc_heap::enque_pinned_plug (uint8_t* plug,
}
}
- dprintf (3, ("enquing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d",
+ dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d",
mark_stack_tos, &mark_stack_array[mark_stack_tos], plug, mark_stack_bos, last_object_in_last_plug, (save_pre_plug_info_p ? 1 : 0)));
mark& m = mark_stack_array[mark_stack_tos];
m.first = plug;
@@ -21072,7 +21072,7 @@ void gc_heap::compact_loh()
{
if (!heap_segment_read_only_p (seg))
{
- // We grew the segment to accommondate allocations.
+ // We grew the segment to accommodate allocations.
if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
{
if ((heap_segment_plan_allocated (seg) - plug_skew) > heap_segment_used (seg))
@@ -21292,7 +21292,7 @@ void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p,
artificial_pinned_size = ps;
}
-// Because we have the artifical pinning, we can't gaurantee that pinned and npinned
+// Because we have the artificial pinning, we can't guarantee that pinned and npinned
// plugs are always interleaved.
void gc_heap::store_plug_gap_info (uint8_t* plug_start,
uint8_t* plug_end,
@@ -24443,7 +24443,7 @@ void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL co
clear_card_for_addresses (dest, dest + len);
}
-// POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
+// POPO TODO: We should actually just recover the artificially made gaps here..because when we copy
// we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
// we won't need to individually recover each overwritten part of plugs.
inline
@@ -25430,7 +25430,7 @@ BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
// the mark_array flag for these segments will remain the same.
BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
{
- dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
+ dprintf (GC_TABLE_LOG, ("committing existing segs on MA %Ix", new_mark_array_addr));
generation* gen = generation_of (max_generation);
heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
while (1)
@@ -27942,7 +27942,7 @@ void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
{
deque_pinned_plug();
update_oldest_pinned_plug();
- dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
+ dprintf (3, ("deque pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
}
else
{
@@ -29865,7 +29865,7 @@ size_t gc_heap::joined_youngest_desired (size_t new_allocation)
{
uint32_t memory_load = 0;
get_memory_info (&memory_load);
- dprintf (2, ("Current emory load: %d", memory_load));
+ dprintf (2, ("Current memory load: %d", memory_load));
size_t final_total =
trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
@@ -33921,7 +33921,7 @@ static int32_t GCStressCurCount = 0;
static int32_t GCStressStartAtJit = -1;
// the maximum number of foreground GCs we'll induce during one BGC
-// (this number does not include "naturally" occuring GCs).
+// (this number does not include "naturally" occurring GCs).
static int32_t GCStressMaxFGCsPerBGC = -1;
// CLRRandom implementation can produce FPU exceptions if
@@ -34092,7 +34092,7 @@ BOOL GCHeap::StressHeap(gc_alloc_context * context)
if (str)
{
// Chop off the end of the string and form a new object out of it.
- // This will 'free' an object at the begining of the heap, which will
+ // This will 'free' an object at the beginning of the heap, which will
// force data movement. Note that we can only do this so many times.
// before we have to move on to the next string.
unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
@@ -34924,7 +34924,7 @@ void gc_heap::record_interesting_info_per_heap()
heap_number,
(size_t)settings.gc_index,
settings.condemned_generation,
- // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
+ // TEMP - I am just doing this for wks GC 'cause I wanna see the pattern of doing C/S GCs.
(settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
((expand_mechanism >= 0)? "X" : ""), // EX
((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
@@ -35262,7 +35262,7 @@ GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
size_t GCHeap::GetTotalBytesInUse ()
{
#ifdef MULTIPLE_HEAPS
- //enumarate all the heaps and get their size.
+ //enumerate all the heaps and get their size.
size_t tot_size = 0;
for (int i = 0; i < gc_heap::n_heaps; i++)
{
@@ -35466,10 +35466,10 @@ int GCHeap::GetLOHCompactionMode()
return pGenGCHeap->loh_compaction_mode;
}
-void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
+void GCHeap::SetLOHCompactionMode (int newLOHCompactionMode)
{
#ifdef FEATURE_LOH_COMPACTION
- pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
+ pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionMode;
#endif //FEATURE_LOH_COMPACTION
}
@@ -35581,7 +35581,7 @@ HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
counters->promoted_size = 0;
counters->collection_count = 0;
- //enumarate all the heaps and get their counters.
+ //enumerate all the heaps and get their counters.
for (int i = 0; i < gc_heap::n_heaps; i++)
{
dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
@@ -35674,7 +35674,7 @@ Object* GCHeap::GetNextFinalizableObject()
if (O)
return O;
}
- //return the first non crtitical/critical one in the first queue.
+ //return the first non critical/critical one in the first queue.
for (int hn = 0; hn < gc_heap::n_heaps; hn++)
{
gc_heap* hp = gc_heap::g_heaps [hn];
diff --git a/src/Native/gc/handletable.cpp b/src/Native/gc/handletable.cpp
index 3be1c1611..2b598528c 100644
--- a/src/Native/gc/handletable.cpp
+++ b/src/Native/gc/handletable.cpp
@@ -1056,7 +1056,7 @@ void HndScanHandlesForGC(HHANDLETABLE hTable, HANDLESCANPROC scanProc, uintptr_t
/*
* HndResetAgeMap
*
- * Service to forceably reset the age map for a set of handles.
+ * Service to forcibly reset the age map for a set of handles.
*
* Provided for GC-time resetting the handle table's write barrier. This is not
* normally advisable, as it increases the amount of work that will be done in
diff --git a/src/Native/gc/handletablecore.cpp b/src/Native/gc/handletablecore.cpp
index 5776c26ac..4137c1645 100644
--- a/src/Native/gc/handletablecore.cpp
+++ b/src/Native/gc/handletablecore.cpp
@@ -129,7 +129,7 @@ void QuickSort(uintptr_t *pData, int left, int right, PFNCOMPARE pfnCompare)
*
* Returns:
* <0 - handle P should be freed before handle Q
- * =0 - handles are eqivalent for free order purposes
+ * =0 - handles are equivalent for free order purposes
* >0 - handle Q should be freed before handle P
*
*/
@@ -242,7 +242,7 @@ BOOL TableCanFreeSegmentNow(HandleTable *pTable, TableSegment *pSegment)
_ASSERTE(threadId.IsCurrentThread());
#endif // _DEBUG
- // deterine if any segment is currently being scanned asynchronously
+ // determine if any segment is currently being scanned asynchronously
TableSegment *pSegmentAsync = NULL;
// do we have async info?
@@ -1852,7 +1852,7 @@ void SegmentTrimExcessPages(TableSegment *pSegment)
// compute the address for the new decommit line
size_t dwDecommitAddr = dwLo - g_SystemInfo.dwPageSize;
- // assume a decommit line of zero until we know otheriwse
+ // assume a decommit line of zero until we know otherwise
uDecommitLine = 0;
// if the address is within the handle area then compute the line from the address
@@ -1869,7 +1869,7 @@ void SegmentTrimExcessPages(TableSegment *pSegment)
/*
* BlockAllocHandlesInMask
*
- * Attempts to allocate the requested number of handes of the specified type,
+ * Attempts to allocate the requested number of handles of the specified type,
* from the specified mask of the specified handle block.
*
* Returns the number of available handles actually allocated.
@@ -2029,7 +2029,7 @@ uint32_t BlockAllocHandlesInitial(TableSegment *pSegment, uint32_t uType, uint32
/*
* BlockAllocHandles
*
- * Attempts to allocate the requested number of handes of the specified type,
+ * Attempts to allocate the requested number of handles of the specified type,
* from the specified handle block.
*
* Returns the number of available handles actually allocated.
@@ -2087,7 +2087,7 @@ uint32_t BlockAllocHandles(TableSegment *pSegment, uint32_t uBlock, OBJECTHANDLE
/*
* SegmentAllocHandlesFromTypeChain
*
- * Attempts to allocate the requested number of handes of the specified type,
+ * Attempts to allocate the requested number of handles of the specified type,
* from the specified segment's block chain for the specified type. This routine
* ONLY scavenges existing blocks in the type chain. No new blocks are committed.
*
@@ -2171,7 +2171,7 @@ uint32_t SegmentAllocHandlesFromTypeChain(TableSegment *pSegment, uint32_t uType
/*
* SegmentAllocHandlesFromFreeList
*
- * Attempts to allocate the requested number of handes of the specified type,
+ * Attempts to allocate the requested number of handles of the specified type,
* by committing blocks from the free list to that type's type chain.
*
* Returns the number of available handles actually allocated.
@@ -2230,7 +2230,7 @@ uint32_t SegmentAllocHandlesFromFreeList(TableSegment *pSegment, uint32_t uType,
/*
* SegmentAllocHandles
*
- * Attempts to allocate the requested number of handes of the specified type,
+ * Attempts to allocate the requested number of handles of the specified type,
* from the specified segment.
*
* Returns the number of available handles actually allocated.
@@ -2268,7 +2268,7 @@ uint32_t SegmentAllocHandles(TableSegment *pSegment, uint32_t uType, OBJECTHANDL
/*
* TableAllocBulkHandles
*
- * Attempts to allocate the requested number of handes of the specified type.
+ * Attempts to allocate the requested number of handles of the specified type.
*
* Returns the number of handles that were actually allocated. This is always
* the same as the number of handles requested except in out-of-memory conditions,
diff --git a/src/Native/gc/handletablescan.cpp b/src/Native/gc/handletablescan.cpp
index 86ce62d5b..504630b9a 100644
--- a/src/Native/gc/handletablescan.cpp
+++ b/src/Native/gc/handletablescan.cpp
@@ -46,7 +46,7 @@ MaskDWORD is also non-zero.
2. AgeEphemeral. When Ephemeral GC happens, ages for handles which belong to the GC condemned generation should be
incremented by 1. The operation is done by calculating a new uint32_t using the old uint32_t value:
NewGenerationDWORD = COMPUTE_AGED_CLUMPS(OldGenerationDWORD, BuildAgeMask(condemnedGeneration, MaxGen))
-so that if a byte in OldGenerationDWORD is smaller than or equals to condemnedGeneration. the coresponding byte in
+so that if a byte in OldGenerationDWORD is smaller than or equals to condemnedGeneration. the corresponding byte in
NewGenerationDWORD is 1 bigger than the old value, otherwise it remains unchanged.
3. Age. Similar as AgeEphemeral, but we use a special mask if condemned generation is max gen (2):
@@ -118,7 +118,7 @@ If you change any of those algorithm, please verify it by this program:
assert (mask == 0);
return;
}
- //any generaion bigger than 2 is actually 2
+ //any generation bigger than 2 is actually 2
if (gen > 2)
gen = 2;
@@ -716,10 +716,10 @@ void CALLBACK BlockScanBlocksEphemeral(PTR_TableSegment pSegment, uint32_t uBloc
uint32_t *pdwGen = (uint32_t *)pSegment->rgGeneration + uBlock;
uint32_t *pdwGenLast = pdwGen + uCount;
- // loop over all the blocks, checking for elligible clumps as we go
+ // loop over all the blocks, checking for eligible clumps as we go
do
{
- // determine if any clumps in this block are elligible
+ // determine if any clumps in this block are eligible
uint32_t dwClumpMask = COMPUTE_CLUMP_MASK(*pdwGen, dwAgeMask);
// if there are any clumps to scan then scan them now
@@ -1434,7 +1434,7 @@ PTR_TableSegment CALLBACK StandardSegmentIterator(PTR_HandleTable pTable, PTR_Ta
PTR_TableSegment pNextSegment = QuickSegmentIterator(pTable, pPrevSegment);
#ifndef DACCESS_COMPILE
- // re-sort the block chains if neccessary
+ // re-sort the block chains if necessary
if (pNextSegment && pNextSegment->fResortChains)
SegmentResortChains(pNextSegment);
#endif
@@ -1630,7 +1630,7 @@ void SegmentScanByTypeChain(PTR_TableSegment pSegment, uint32_t uType, BLOCKSCAN
} while ((uNext == uLast) && (uNext != uHead));
- // call the calback for this group of blocks
+ // call the callback for this group of blocks
pfnBlockHandler(pSegment, uBlock, (uLast - uBlock), pInfo);
// advance to the next block
@@ -1694,7 +1694,7 @@ void SegmentScanByTypeMap(PTR_TableSegment pSegment, const BOOL *rgTypeInclusion
break;
}
- // call the calback for the group of blocks we found
+ // call the callback for the group of blocks we found
pfnBlockHandler(pSegment, uFirst, (uBlock - uFirst), pInfo);
// look for another range starting with the next block
diff --git a/src/Native/gc/objecthandle.cpp b/src/Native/gc/objecthandle.cpp
index e8eed9300..28dcc5222 100644
--- a/src/Native/gc/objecthandle.cpp
+++ b/src/Native/gc/objecthandle.cpp
@@ -84,7 +84,7 @@ void CALLBACK PromoteRefCounted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtra
WRAPPER_NO_CONTRACT;
UNREFERENCED_PARAMETER(pExtraInfo);
- // there are too many races when asychnronously scanning ref-counted handles so we no longer support it
+ // there are too many races when asynchronously scanning ref-counted handles so we no longer support it
_ASSERTE(!((ScanContext*)lp1)->concurrent);
LOG((LF_GC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("", pObjRef, "causes promotion of ", *pObjRef)));
@@ -1247,7 +1247,7 @@ void Ref_CheckReachable(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
// strong handle to refer to the secondary as this could case a cycle in the graph if the secondary somehow
// pointed back to the primary. Can't use weak handle because that would not keep the secondary object alive.
//
-// The result is that a dependenHandle has the EFFECT of
+// The result is that a dependentHandle has the EFFECT of
// * long weak handles in both the primary and secondary objects
// * a strong reference from the primary object to the secondary one
//
diff --git a/src/Native/gc/unix/gcenv.unix.cpp b/src/Native/gc/unix/gcenv.unix.cpp
index 0b4ab0ed1..07259c0f2 100644
--- a/src/Native/gc/unix/gcenv.unix.cpp
+++ b/src/Native/gc/unix/gcenv.unix.cpp
@@ -11,7 +11,7 @@
// This isn't something we want, because we're totally fine using non-posix functions.
#if defined(__APPLE__)
#define _DARWIN_C_SOURCE
-#endif // definfed(__APPLE__)
+#endif // defined(__APPLE__)
#include <pthread.h>
#include <signal.h>
@@ -69,7 +69,7 @@ static const int tccMilliSecondsToMicroSeconds = 1000;
// The number of nanoseconds in a millisecond.
static const int tccMilliSecondsToNanoSeconds = 1000000;
-// The cachced number of logical CPUs observed.
+// The cached number of logical CPUs observed.
static uint32_t g_logicalCpuCount = 0;
// Helper memory page used by the FlushProcessWriteBuffers
diff --git a/src/Native/gen-buildsys-clang.sh b/src/Native/gen-buildsys-clang.sh
index 0c2441f77..fed57d122 100755
--- a/src/Native/gen-buildsys-clang.sh
+++ b/src/Native/gen-buildsys-clang.sh
@@ -117,6 +117,7 @@ if [ $build_arch == "wasm" ]; then
emcmake cmake \
"-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1" \
"-DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake" \
+ "-DCLR_CMAKE_TARGET_ARCH=$build_arch" \
"-DCMAKE_BUILD_TYPE=$build_type" \
"$1/src/Native"
else
diff --git a/src/Native/jitinterface/jitwrapper.cpp b/src/Native/jitinterface/jitwrapper.cpp
index 3b1532124..ac5264b7d 100644
--- a/src/Native/jitinterface/jitwrapper.cpp
+++ b/src/Native/jitinterface/jitwrapper.cpp
@@ -27,11 +27,11 @@ private:
unsigned __int64 corJitFlags;
};
-static const GUID JITEEVersionIdentifier = { /* a6860f80-01cb-4f87-82c2-a8e5a744f2fa */
- 0xa6860f80,
- 0x01cb,
- 0x4f87,
- {0x82, 0xc2, 0xa8, 0xe5, 0xa7, 0x44, 0xf2, 0xfa}
+static const GUID JITEEVersionIdentifier = { /* 0ba106c8-81a0-407f-99a1-928448c1eb62 */
+ 0x0ba106c8,
+ 0x81a0,
+ 0x407f,
+ {0x99, 0xa1, 0x92, 0x84, 0x48, 0xc1, 0xeb, 0x62}
};
class Jit
diff --git a/src/Native/libunwind/src/Unwind-EHABI.cpp b/src/Native/libunwind/src/Unwind-EHABI.cpp
index 863953eb2..18b82f8c8 100644
--- a/src/Native/libunwind/src/Unwind-EHABI.cpp
+++ b/src/Native/libunwind/src/Unwind-EHABI.cpp
@@ -695,7 +695,7 @@ _LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) {
/// may force a jump to a landing pad in that function, the landing
/// pad code may then call _Unwind_Resume() to continue with the
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
-/// geneated user code. All other _Unwind_* routines are called
+/// generated user code. All other _Unwind_* routines are called
/// by the C++ runtime __cxa_* routines.
///
/// Note: re-throwing an exception (as opposed to continuing the unwind)
diff --git a/src/Native/libunwind/src/Unwind_AppleExtras.cpp b/src/Native/libunwind/src/Unwind_AppleExtras.cpp
index d8301c057..dcde35eec 100644
--- a/src/Native/libunwind/src/Unwind_AppleExtras.cpp
+++ b/src/Native/libunwind/src/Unwind_AppleExtras.cpp
@@ -191,7 +191,7 @@ bool checkKeyMgrRegisteredFDEs(uintptr_t pc, void *&fde) {
_Unwind_FunctionContext *fc_ = nullptr;
#endif
-// Accessors to get get/set linked list of frames for sjlj based execeptions.
+// Accessors to get get/set linked list of frames for sjlj based exceptions.
_LIBUNWIND_HIDDEN
struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() {
#ifndef _LIBUNWIND_HAS_NO_THREADS
diff --git a/src/Native/probe-win.ps1 b/src/Native/probe-win.ps1
index 70030b14d..55f3d329f 100644
--- a/src/Native/probe-win.ps1
+++ b/src/Native/probe-win.ps1
@@ -6,7 +6,7 @@ function GetCMakeVersions
$items = @()
$items += @(Get-ChildItem hklm:\SOFTWARE\Wow6432Node\Kitware -ErrorAction SilentlyContinue)
$items += @(Get-ChildItem hklm:\SOFTWARE\Kitware -ErrorAction SilentlyContinue)
- return $items | where { $_.PSChildName.StartsWith("CMake ") }
+ return $items | where { $_.PSChildName.StartsWith("CMake") }
}
function GetCMakeInfo($regKey)
@@ -17,7 +17,13 @@ function GetCMakeInfo($regKey)
catch {
return $null
}
- $cmakeDir = (Get-ItemProperty $regKey.PSPath).'(default)'
+ $itemProperty = Get-ItemProperty $regKey.PSPath;
+ if (Get-Member -inputobject $itemProperty -name "InstallDir" -Membertype Properties) {
+ $cmakeDir = $itemProperty.InstallDir
+ }
+ else {
+ $cmakeDir = $itemProperty.'(default)'
+ }
$cmakePath = [System.IO.Path]::Combine($cmakeDir, "bin\cmake.exe")
if (![System.IO.File]::Exists($cmakePath)) {
return $null
@@ -28,9 +34,13 @@ function GetCMakeInfo($regKey)
function LocateCMake
{
$errorMsg = "CMake is a pre-requisite to build this repository but it was not found on the path. Please install CMake from http://www.cmake.org/download/ and ensure it is on your path."
- $inPathPath = (get-command cmake.exe -ErrorAction SilentlyContinue).Path
+ $inPathPath = (get-command cmake.exe -ErrorAction SilentlyContinue)
if ($inPathPath -ne $null) {
- return $inPathPath
+ # Resolve the first version of CMake if multiple commands are found
+ if ($inPathPath.Length -gt 1) {
+ return $inPathPath[0].Path
+ }
+ return $inPathPath.Path
}
# Let us hope that CMake keep using their current version scheme
$validVersions = @()
diff --git a/src/Runtime.Base/src/Runtime.Base.csproj b/src/Runtime.Base/src/Runtime.Base.csproj
index ffae8483f..daa602215 100644
--- a/src/Runtime.Base/src/Runtime.Base.csproj
+++ b/src/Runtime.Base/src/Runtime.Base.csproj
@@ -112,7 +112,7 @@
</ItemGroup>
<ItemGroup>
<Compile Condition="'$(Platform)'!='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Full\AsmOffsets.cs" />
- <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsets.cs" />
+ <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsetsPortable.cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
diff --git a/src/Runtime.Base/src/System/Diagnostics/Eval.cs b/src/Runtime.Base/src/System/Diagnostics/Eval.cs
index eaa76dfe4..ecb535876 100644
--- a/src/Runtime.Base/src/System/Diagnostics/Eval.cs
+++ b/src/Runtime.Base/src/System/Diagnostics/Eval.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Diagnostics;
using System.Runtime;
using System.Runtime.CompilerServices;
@@ -10,20 +11,13 @@ namespace System.Diagnostics
{
public static class Eval
{
- private static IntPtr s_highLevelDebugFuncEvalHelper;
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- [RuntimeExport("RhpSetHighLevelDebugFuncEvalHelper")]
- public static void SetHighLevelDebugFuncEvalHelper(IntPtr ptr)
- {
- s_highLevelDebugFuncEvalHelper = ptr;
- }
-
[MethodImpl(MethodImplOptions.NoInlining)]
[RuntimeExport("RhpDebugFuncEvalHelper")]
- public static void RhpDebugFuncEvalHelper()
+ public unsafe static void RhpDebugFuncEvalHelper(IntPtr unusedTransitionBlock, IntPtr classlibAddress)
{
- CalliIntrinsics.CallVoid(s_highLevelDebugFuncEvalHelper);
+ IntPtr pDebugFuncEvalHelper = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(classlibAddress, ClassLibFunctionId.DebugFuncEvalHelper);
+ Debug.Assert(pDebugFuncEvalHelper != IntPtr.Zero);
+ CalliIntrinsics.CallVoid(pDebugFuncEvalHelper);
}
}
} \ No newline at end of file
diff --git a/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs b/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs
index d23b9636a..af520c824 100644
--- a/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs
+++ b/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs
@@ -11,12 +11,14 @@ namespace System.Runtime
internal static unsafe partial class CalliIntrinsics
{
internal static void CallVoid(IntPtr pfn) { Call<int>(pfn); }
+ internal static void CallVoid(IntPtr pfn, long arg0) { Call<int>(pfn, arg0); }
internal static void CallVoid(IntPtr pfn, object arg0) { Call<int>(pfn, arg0); }
internal static void CallVoid(IntPtr pfn, IntPtr arg0, object arg1) { Call<int>(pfn, arg0, arg1); }
internal static void CallVoid(IntPtr pfn, RhFailFastReason arg0, object arg1, IntPtr arg2, IntPtr arg3) { Call<int>(pfn, arg0, arg1, arg2, arg3); }
internal static void CallVoid(IntPtr pfn, object arg0, IntPtr arg1, int arg2) { Call<int>(pfn, arg0, arg1, arg2); }
internal static T Call<T>(IntPtr pfn) { throw new NotImplementedException(); }
+ internal static T Call<T>(IntPtr pfn, long arg0) { throw new NotImplementedException(); }
internal static T Call<T>(IntPtr pfn, object arg0) { throw new NotImplementedException(); }
internal static T Call<T>(IntPtr pfn, IntPtr arg0) { throw new NotImplementedException(); }
internal static T Call<T>(IntPtr pfn, IntPtr arg0, object arg1) { throw new NotImplementedException(); }
diff --git a/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs b/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs
index 3a1e8a954..f3b4ff4d4 100644
--- a/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs
+++ b/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs
@@ -25,7 +25,7 @@ namespace Internal.Runtime
{
fixed (EEType* pThis = &this)
{
- IntPtr pGetArrayEEType = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(new IntPtr(pThis), EH.ClassLibFunctionId.GetSystemArrayEEType);
+ IntPtr pGetArrayEEType = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(new IntPtr(pThis), ClassLibFunctionId.GetSystemArrayEEType);
if (pGetArrayEEType != IntPtr.Zero)
return (EEType*)CalliIntrinsics.Call<IntPtr>(pGetArrayEEType);
}
diff --git a/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs
index 761ad5319..4d3cdf674 100644
--- a/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs
+++ b/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs
@@ -9,6 +9,9 @@ using System.Runtime.InteropServices;
using Internal.Runtime;
+// Disable: Filter expression is a constant. We know. We just can't do an unfiltered catch.
+#pragma warning disable 7095
+
namespace System.Runtime
{
public enum RhFailFastReason
@@ -144,20 +147,6 @@ namespace System.Runtime
InternalCalls.RhpFallbackFailFast();
}
- // Constants used with RhpGetClasslibFunction, to indicate which classlib function
- // we are interested in.
- // Note: make sure you change the def in EHHelpers.cpp if you change this!
- internal enum ClassLibFunctionId
- {
- GetRuntimeException = 0,
- FailFast = 1,
- // UnhandledExceptionHandler = 2, // unused
- AppendExceptionStackFrame = 3,
- CheckStaticClassConstruction = 4,
- GetSystemArrayEEType = 5,
- OnFirstChance = 6,
- }
-
// Given an address pointing somewhere into a managed module, get the classlib-defined fail-fast
// function and invoke it. Any failure to find and invoke the function, or if it returns, results in
// MRT-defined fail-fast behavior.
@@ -180,7 +169,7 @@ namespace System.Runtime
// Invoke the classlib fail fast function.
CalliIntrinsics.CallVoid(pFailFastFunction, reason, unhandledException, IntPtr.Zero, IntPtr.Zero);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -228,7 +217,7 @@ namespace System.Runtime
{
CalliIntrinsics.CallVoid(pOnFirstChanceFunction, exception);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -260,7 +249,7 @@ namespace System.Runtime
{
CalliIntrinsics.CallVoid(pFailFastFunction, reason, unhandledException, exInfo._pExContext->IP, (IntPtr)pContext);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -290,7 +279,7 @@ namespace System.Runtime
{
CalliIntrinsics.CallVoid(pAppendStackFrame, exception, IP, flags);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -317,7 +306,7 @@ namespace System.Runtime
{
e = CalliIntrinsics.Call<Exception>(pGetRuntimeExceptionFunction, id);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -353,7 +342,7 @@ namespace System.Runtime
{
e = CalliIntrinsics.Call<Exception>(pGetRuntimeExceptionFunction, id);
}
- catch
+ catch when (true)
{
// disallow all exceptions leaking out of callbacks
}
@@ -875,28 +864,38 @@ namespace System.Runtime
return false;
}
+#if DEBUG && !INPLACE_RUNTIME
private static EEType* s_pLowLevelObjectType;
-
- private static bool ShouldTypedClauseCatchThisException(object exception, EEType* pClauseType)
+ private static void AssertNotRuntimeObject(EEType* pClauseType)
{
- if (TypeCast.IsInstanceOfClass(exception, pClauseType) != null)
- return true;
+ //
+ // The C# try { } catch { } clause expands into a typed catch of System.Object.
+ // Since runtime has its own definition of System.Object, try { } catch { } might not do what
+ // was intended (catch all exceptions).
+ //
+ // This assertion is making sure we don't use try { } catch { } within the runtime.
+ // The runtime codebase should either use try { } catch (Exception) { } for exception types
+ // from the runtime or a try { } catch when (true) { } to catch all exceptions.
+ //
if (s_pLowLevelObjectType == null)
{
- // TODO: Avoid allocating here as that may fail
+ // Allocating might fail, but since this is just a debug assert, it's probably fine.
s_pLowLevelObjectType = new System.Object().EEType;
}
- // This allows the typical try { } catch { }--which expands to a typed catch of System.Object--to work on
- // all objects when the clause is in the low level runtime code. This special case is needed because
- // objects from foreign type systems are sometimes throw back up at runtime code and this is the only way
- // to catch them outside of having a filter with no type check in it, which isn't currently possible to
- // write in C#. See https://github.com/dotnet/roslyn/issues/4388
- if (pClauseType->IsEquivalentTo(s_pLowLevelObjectType))
- return true;
+ Debug.Assert(!pClauseType->IsEquivalentTo(s_pLowLevelObjectType));
+ }
+#endif // DEBUG && !INPLACE_RUNTIME
+
- return false;
+ private static bool ShouldTypedClauseCatchThisException(object exception, EEType* pClauseType)
+ {
+#if DEBUG && !INPLACE_RUNTIME
+ AssertNotRuntimeObject(pClauseType);
+#endif
+
+ return TypeCast.IsInstanceOfClass(exception, pClauseType) != null;
}
private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart)
diff --git a/src/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
index 0c4cc3ebf..e647e3ab0 100644
--- a/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
+++ b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
@@ -30,6 +30,22 @@ namespace System.Runtime
public uint VTableOffset;
}
+ // Constants used with RhpGetClasslibFunction, to indicate which classlib function
+ // we are interested in.
+ // Note: make sure you change the def in ICodeManager.h if you change this!
+ internal enum ClassLibFunctionId
+ {
+ GetRuntimeException = 0,
+ FailFast = 1,
+ // UnhandledExceptionHandler = 2, // unused
+ AppendExceptionStackFrame = 3,
+ CheckStaticClassConstruction = 4,
+ GetSystemArrayEEType = 5,
+ OnFirstChance = 6,
+ DebugFuncEvalHelper = 7,
+ DebugFuncEvalAbortHelper = 8,
+ }
+
internal static class InternalCalls
{
//
@@ -232,12 +248,12 @@ namespace System.Runtime
[RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromCodeAddress")]
[MethodImpl(MethodImplOptions.InternalCall)]
[ManuallyManaged(GcPollPolicy.Never)]
- internal extern static unsafe void* RhpGetClasslibFunctionFromCodeAddress(IntPtr address, EH.ClassLibFunctionId id);
+ internal extern static unsafe void* RhpGetClasslibFunctionFromCodeAddress(IntPtr address, ClassLibFunctionId id);
[RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromEEType")]
[MethodImpl(MethodImplOptions.InternalCall)]
[ManuallyManaged(GcPollPolicy.Never)]
- internal extern static unsafe void* RhpGetClasslibFunctionFromEEType(IntPtr pEEType, EH.ClassLibFunctionId id);
+ internal extern static unsafe void* RhpGetClasslibFunctionFromEEType(IntPtr pEEType, ClassLibFunctionId id);
//
// StackFrameIterator
diff --git a/src/Runtime.Base/src/System/Runtime/ThunkPool.cs b/src/Runtime.Base/src/System/Runtime/ThunkPool.cs
index 404494451..76df73e4d 100644
--- a/src/Runtime.Base/src/System/Runtime/ThunkPool.cs
+++ b/src/Runtime.Base/src/System/Runtime/ThunkPool.cs
@@ -135,7 +135,7 @@ namespace System.Runtime
if (newHeap._nextAvailableThunkPtr != IntPtr.Zero)
return newHeap;
}
- catch { }
+ catch (Exception) { }
return null;
}
@@ -156,7 +156,7 @@ namespace System.Runtime
{
newBlockInfo = new AllocatedBlock();
}
- catch
+ catch (Exception)
{
return false;
}
diff --git a/src/System.Private.CoreLib/shared/Internal/IO/File.Unix.cs b/src/System.Private.CoreLib/shared/Internal/IO/File.Unix.cs
new file mode 100644
index 000000000..50fa0f0d0
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Internal/IO/File.Unix.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.
+// See the LICENSE file in the project root for more information.
+
+namespace Internal.IO
+{
+ internal static partial class File
+ {
+ internal static bool InternalExists(string fullPath)
+ {
+ Interop.Sys.FileStatus fileinfo;
+
+ // First use stat, as we want to follow symlinks. If that fails, it could be because the symlink
+ // is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate
+ // based on the symlink itself.
+ if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 &&
+ Interop.Sys.LStat(fullPath, out fileinfo) < 0)
+ {
+ return false;
+ }
+
+ return ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFDIR);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Internal/IO/File.Windows.cs b/src/System.Private.CoreLib/shared/Internal/IO/File.Windows.cs
new file mode 100644
index 000000000..0acae3b45
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Internal/IO/File.Windows.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Internal.IO
+{
+ internal static partial class File
+ {
+ internal static bool InternalExists(string fullPath)
+ {
+ Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA();
+ int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
+
+ return (errorCode == 0) && (data.dwFileAttributes != -1)
+ && ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0);
+ }
+
+ /// <summary>
+ /// Returns 0 on success, otherwise a Win32 error code. Note that
+ /// classes should use -1 as the uninitialized state for dataInitialized.
+ /// </summary>
+ /// <param name="returnErrorOnNotFound">Return the error code for not found errors?</param>
+ internal static int FillAttributeInfo(string path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool returnErrorOnNotFound)
+ {
+ int errorCode = Interop.Errors.ERROR_SUCCESS;
+
+ using (DisableMediaInsertionPrompt.Create())
+ {
+ if (!Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data))
+ {
+ errorCode = Marshal.GetLastWin32Error();
+ if (errorCode == Interop.Errors.ERROR_ACCESS_DENIED)
+ {
+ // Files that are marked for deletion will not let you GetFileAttributes,
+ // ERROR_ACCESS_DENIED is given back without filling out the data struct.
+ // FindFirstFile, however, will. Historically we always gave back attributes
+ // for marked-for-deletion files.
+
+ var findData = new Interop.Kernel32.WIN32_FIND_DATA();
+ using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(path, ref findData))
+ {
+ if (handle.IsInvalid)
+ {
+ errorCode = Marshal.GetLastWin32Error();
+ }
+ else
+ {
+ errorCode = Interop.Errors.ERROR_SUCCESS;
+ data.PopulateFrom(ref findData);
+ }
+ }
+ }
+ }
+ }
+
+ if (errorCode != Interop.Errors.ERROR_SUCCESS && !returnErrorOnNotFound)
+ {
+ switch (errorCode)
+ {
+ case Interop.Errors.ERROR_FILE_NOT_FOUND:
+ case Interop.Errors.ERROR_PATH_NOT_FOUND:
+ case Interop.Errors.ERROR_NOT_READY: // Removable media not ready
+ // Return default value for backward compatibility
+ data.dwFileAttributes = -1;
+ return Interop.Errors.ERROR_SUCCESS;
+ }
+ }
+
+ return errorCode;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/IO/InternalFile.cs b/src/System.Private.CoreLib/shared/Internal/IO/File.cs
index 726e25419..2fcc0f391 100644
--- a/src/System.Private.CoreLib/src/System/IO/InternalFile.cs
+++ b/src/System.Private.CoreLib/shared/Internal/IO/File.cs
@@ -2,18 +2,24 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Diagnostics;
using System.Security;
+using System.IO;
-namespace System.IO
+namespace Internal.IO
{
- internal static partial class InternalFile
+ //
+ // Subsetted clone of System.IO.File for internal runtime use.
+ // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.IO.FileSystem.
+ //
+ internal static partial class File
{
// Tests if a file exists. The result is true if the file
// given by the specified path exists; otherwise, the result is
// false. Note that if path describes a directory,
// Exists will return true.
- public static bool Exists(String path)
+ public static bool Exists(string path)
{
try
{
@@ -43,5 +49,29 @@ namespace System.IO
return false;
}
+
+ public static byte[] ReadAllBytes(string path)
+ {
+ // bufferSize == 1 used to avoid unnecessary buffer in FileStream
+ using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1))
+ {
+ long fileLength = fs.Length;
+ if (fileLength > int.MaxValue)
+ throw new IOException(SR.IO_FileTooLong2GB);
+
+ int index = 0;
+ int count = (int)fileLength;
+ byte[] bytes = new byte[count];
+ while (count > 0)
+ {
+ int n = fs.Read(bytes, index, count);
+ if (n == 0)
+ throw Error.GetEndOfFile();
+ index += n;
+ count -= n;
+ }
+ return bytes;
+ }
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs b/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs
index cd3dd052a..aeff3ce2c 100644
--- a/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs
+++ b/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs
@@ -169,6 +169,46 @@ namespace Internal.Runtime.CompilerServices
}
/// <summary>
+ /// Determines whether the memory address referenced by <paramref name="left"/> is greater than
+ /// the memory address referenced by <paramref name="right"/>.
+ /// </summary>
+ /// <remarks>
+ /// This check is conceptually similar to "(void*)(&amp;left) &gt; (void*)(&amp;right)".
+ /// </remarks>
+ [Intrinsic]
+ [NonVersionable]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAddressGreaterThan<T>(ref T left, ref T right)
+ {
+ throw new PlatformNotSupportedException();
+
+ // ldarg.0
+ // ldarg.1
+ // cgt.un
+ // ret
+ }
+
+ /// <summary>
+ /// Determines whether the memory address referenced by <paramref name="left"/> is less than
+ /// the memory address referenced by <paramref name="right"/>.
+ /// </summary>
+ /// <remarks>
+ /// This check is conceptually similar to "(void*)(&amp;left) &lt; (void*)(&amp;right)".
+ /// </remarks>
+ [Intrinsic]
+ [NonVersionable]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAddressLessThan<T>(ref T left, ref T right)
+ {
+ throw new PlatformNotSupportedException();
+
+ // ldarg.0
+ // ldarg.1
+ // clt.un
+ // ret
+ }
+
+ /// <summary>
/// Initializes a block of memory at the given location with a given initial value
/// without assuming architecture dependent alignment of the address.
/// </summary>
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/Interop.Libraries.cs b/src/System.Private.CoreLib/shared/Interop/Unix/Interop.Libraries.cs
index 7b3dea453..02d009244 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/Interop.Libraries.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/Interop.Libraries.cs
@@ -6,7 +6,7 @@ internal static partial class Interop
{
internal static partial class Libraries
{
- internal const string GlobalizationInterop = "System.Globalization.Native";
+ internal const string GlobalizationNative = "System.Globalization.Native";
internal const string SystemNative = "System.Native";
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs
index 7b3caeabd..55553cc7e 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs
@@ -9,25 +9,25 @@ using System.Text;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
internal delegate void EnumCalendarInfoCallback(
[MarshalAs(UnmanagedType.LPWStr)] string calendarString,
IntPtr context);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendars")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendars")]
internal static extern int GetCalendars(string localeName, CalendarId[] calendars, int calendarsCapacity);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendarInfo")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendarInfo")]
internal static extern ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, [Out] StringBuilder result, int resultCapacity);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EnumCalendarInfo")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EnumCalendarInfo")]
internal static extern bool EnumCalendarInfo(EnumCalendarInfoCallback callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context);
- [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetLatestJapaneseEra")]
+ [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLatestJapaneseEra")]
internal static extern int GetLatestJapaneseEra();
- [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetJapaneseEraStartDate")]
+ [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetJapaneseEraStartDate")]
internal static extern bool GetJapaneseEraStartDate(int era, out int startYear, out int startMonth, out int startDay);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Casing.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Casing.cs
index 769506b8f..25536d483 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Casing.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Casing.cs
@@ -9,15 +9,15 @@ using System.Text;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCase")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCase")]
internal unsafe static extern void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseInvariant")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseInvariant")]
internal unsafe static extern void ChangeCaseInvariant(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseTurkish")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseTurkish")]
internal unsafe static extern void ChangeCaseTurkish(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
index 683845dbc..08aa6113d 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
@@ -9,41 +9,53 @@ using System.Security;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortHandle")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortHandle")]
internal unsafe static extern ResultCode GetSortHandle(byte[] localeName, out SafeSortHandle sortHandle);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CloseSortHandle")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CloseSortHandle")]
internal unsafe static extern void CloseSortHandle(IntPtr handle);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareString")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareString")]
internal unsafe static extern int CompareString(SafeSortHandle sortHandle, char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len, CompareOptions options);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOf")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOf")]
internal unsafe static extern int IndexOf(SafeSortHandle sortHandle, string target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options, int* matchLengthPtr);
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOf")]
+ internal unsafe static extern int IndexOf(SafeSortHandle sortHandle, char* target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options, int* matchLengthPtr);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_LastIndexOf")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_LastIndexOf")]
internal unsafe static extern int LastIndexOf(SafeSortHandle sortHandle, string target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
internal unsafe static extern int IndexOfOrdinalIgnoreCase(string target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast);
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
+ internal unsafe static extern int IndexOfOrdinalIgnoreCase(char* target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_StartsWith")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_StartsWith")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal unsafe static extern bool StartsWith(SafeSortHandle sortHandle, char* target, int cwTargetLength, char* source, int cwSourceLength, CompareOptions options);
+
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EndsWith")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal unsafe static extern bool EndsWith(SafeSortHandle sortHandle, char* target, int cwTargetLength, char* source, int cwSourceLength, CompareOptions options);
+
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_StartsWith")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool StartsWith(SafeSortHandle sortHandle, string target, int cwTargetLength, string source, int cwSourceLength, CompareOptions options);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EndsWith")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EndsWith")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool EndsWith(SafeSortHandle sortHandle, string target, int cwTargetLength, string source, int cwSourceLength, CompareOptions options);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortKey")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortKey")]
internal unsafe static extern int GetSortKey(SafeSortHandle sortHandle, string str, int strLength, byte* sortKey, int sortKeyLength, CompareOptions options);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareStringOrdinalIgnoreCase")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareStringOrdinalIgnoreCase")]
internal unsafe static extern int CompareStringOrdinalIgnoreCase(char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len);
- [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetSortVersion")]
+ [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetSortVersion")]
internal static extern int GetSortVersion(SafeSortHandle sortHandle);
internal class SafeSortHandle : SafeHandle
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
index c69088414..a16c813b2 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
@@ -8,9 +8,9 @@ using System.Runtime.CompilerServices;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
- [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_LoadICU")]
+ [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICU")]
internal static extern int LoadICU();
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Idna.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Idna.cs
index 43c72281a..af2373946 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Idna.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Idna.cs
@@ -7,15 +7,15 @@ using System.Runtime.InteropServices;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
internal const int AllowUnassigned = 0x1;
internal const int UseStd3AsciiRules = 0x2;
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToAscii")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToAscii")]
internal static unsafe extern int ToAscii(uint flags, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToUnicode")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToUnicode")]
internal static unsafe extern int ToUnicode(uint flags, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
index fcea708ee..09527f0a0 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
@@ -8,33 +8,33 @@ using System.Text;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleName")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleName")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetLocaleName(string localeName, [Out] StringBuilder value, int valueLength);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoString")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoString")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetLocaleInfoString(string localeName, uint localeStringData, [Out] StringBuilder value, int valueLength);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetDefaultLocaleName")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetDefaultLocaleName")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleTimeFormat")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleTimeFormat")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoInt")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoInt")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetLocaleInfoInt(string localeName, uint localeNumberData, ref int value);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoGroupingSizes")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoGroupingSizes")]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetLocaleInfoGroupingSizes(string localeName, uint localeGroupingData, ref int primaryGroupSize, ref int secondaryGroupSize);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocales")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocales")]
internal unsafe static extern int GetLocales([Out] Char[] value, int valueLength);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs
index c4cb9fb85..d442da0ea 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs
@@ -8,12 +8,12 @@ using System.Text;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IsNormalized")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IsNormalized")]
internal static extern int IsNormalized(NormalizationForm normalizationForm, string src, int srcLen);
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_NormalizeString")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_NormalizeString")]
internal static extern int NormalizeString(NormalizationForm normalizationForm, string src, int srcLen, [Out] char[] dstBuffer, int dstBufferCapacity);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs
index cca6ae4dc..4a9933f92 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs
@@ -4,7 +4,7 @@
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
// needs to be kept in sync with ResultCode in System.Globalization.Native
internal enum ResultCode
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs
index 271ec3f9d..47cf26662 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs
@@ -7,7 +7,7 @@ using System.Text;
internal static partial class Interop
{
- internal static partial class GlobalizationInterop
+ internal static partial class Globalization
{
// needs to be kept in sync with TimeZoneDisplayNameType in System.Globalization.Native
internal enum TimeZoneDisplayNameType
@@ -17,7 +17,7 @@ internal static partial class Interop
DaylightSavings = 2,
}
- [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetTimeZoneDisplayName")]
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetTimeZoneDisplayName")]
internal static extern ResultCode GetTimeZoneDisplayName(
string localeName,
string timeZoneId,
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs
index 33b10c0d7..9887bd4f0 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs
@@ -13,7 +13,7 @@ internal static partial class Interop
/// increasing buffer until the size is big enough.
/// </summary>
internal static bool CallStringMethod<TArg1, TArg2, TArg3>(
- Func<TArg1, TArg2, TArg3, StringBuilder, GlobalizationInterop.ResultCode> interopCall,
+ Func<TArg1, TArg2, TArg3, StringBuilder, Interop.Globalization.ResultCode> interopCall,
TArg1 arg1,
TArg2 arg2,
TArg3 arg3,
@@ -26,14 +26,14 @@ internal static partial class Interop
for (int i = 0; i < maxDoubleAttempts; i++)
{
- GlobalizationInterop.ResultCode resultCode = interopCall(arg1, arg2, arg3, stringBuilder);
+ Interop.Globalization.ResultCode resultCode = interopCall(arg1, arg2, arg3, stringBuilder);
- if (resultCode == GlobalizationInterop.ResultCode.Success)
+ if (resultCode == Interop.Globalization.ResultCode.Success)
{
result = StringBuilderCache.GetStringAndRelease(stringBuilder);
return true;
}
- else if (resultCode == GlobalizationInterop.ResultCode.InsufficentBuffer)
+ else if (resultCode == Interop.Globalization.ResultCode.InsufficentBuffer)
{
// increase the string size and loop
stringBuilder.EnsureCapacity(stringBuilder.Capacity * 2);
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs
index eb9e32db0..7213cb026 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs
@@ -9,8 +9,6 @@ internal static partial class Interop
{
internal static partial class Sys
{
- internal static int DEFAULT_PC_NAME_MAX = 255;
-
internal enum PathConfName : int
{
PC_LINK_MAX = 1,
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadDir.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadDir.cs
new file mode 100644
index 000000000..d98c4285c
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadDir.cs
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ private static readonly int s_readBufferSize = GetReadDirRBufferSize();
+
+ internal enum NodeType : int
+ {
+ DT_UNKNOWN = 0,
+ DT_FIFO = 1,
+ DT_CHR = 2,
+ DT_DIR = 4,
+ DT_BLK = 6,
+ DT_REG = 8,
+ DT_LNK = 10,
+ DT_SOCK = 12,
+ DT_WHT = 14
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private unsafe struct InternalDirectoryEntry
+ {
+ internal IntPtr Name;
+ internal int NameLength;
+ internal NodeType InodeType;
+ }
+
+ internal struct DirectoryEntry
+ {
+ internal NodeType InodeType;
+ internal string InodeName;
+ }
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_OpenDir", SetLastError = true)]
+ internal static extern Microsoft.Win32.SafeHandles.SafeDirectoryHandle OpenDir(string path);
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetReadDirRBufferSize", SetLastError = false)]
+ internal static extern int GetReadDirRBufferSize();
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadDirR", SetLastError = false)]
+ private static extern unsafe int ReadDirR(IntPtr dir, byte* buffer, int bufferSize, out InternalDirectoryEntry outputEntry);
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CloseDir", SetLastError = true)]
+ internal static extern int CloseDir(IntPtr dir);
+
+ // The calling pattern for ReadDir is described in src/Native/System.Native/pal_readdir.cpp
+ internal static int ReadDir(SafeDirectoryHandle dir, out DirectoryEntry outputEntry)
+ {
+ bool addedRef = false;
+ try
+ {
+ // We avoid a native string copy into InternalDirectoryEntry.
+ // - If the platform suppors reading into a buffer, the data is read directly into the buffer. The
+ // data can be read as long as the buffer is valid.
+ // - If the platform does not support reading into a buffer, the information returned in
+ // InternalDirectoryEntry points to native memory owned by the SafeDirectoryHandle. The data is only
+ // valid until the next call to CloseDir/ReadDir. We extend the reference until we have copied all data
+ // to ensure it does not become invalid by a CloseDir; and we copy the data so our caller does not
+ // use the native memory held by the SafeDirectoryHandle.
+ dir.DangerousAddRef(ref addedRef);
+
+ unsafe
+ {
+ // s_readBufferSize is zero when the native implementation does not support reading into a buffer.
+ byte* buffer = stackalloc byte[s_readBufferSize];
+ InternalDirectoryEntry temp;
+ int ret = ReadDirR(dir.DangerousGetHandle(), buffer, s_readBufferSize, out temp);
+ // We copy data into DirectoryEntry to ensure there are no dangling references.
+ outputEntry = ret == 0 ?
+ new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } :
+ default(DirectoryEntry);
+
+ return ret;
+ }
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ dir.DangerousRelease();
+ }
+ }
+ }
+
+ private static unsafe string GetDirectoryEntryName(InternalDirectoryEntry dirEnt)
+ {
+ if (dirEnt.NameLength == -1)
+ return Marshal.PtrToStringAnsi(dirEnt.Name);
+ else
+ return Marshal.PtrToStringAnsi(dirEnt.Name, dirEnt.NameLength);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.Stat.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.Stat.cs
index cda00ac6c..0ca199b70 100644
--- a/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.Stat.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.Stat.cs
@@ -24,9 +24,13 @@ internal static partial class Interop
internal uint Gid;
internal long Size;
internal long ATime;
+ internal long ATimeNsec;
internal long MTime;
+ internal long MTimeNsec;
internal long CTime;
+ internal long CTimeNsec;
internal long BirthTime;
+ internal long BirthTimeNsec;
internal long Dev;
internal long Ino;
}
@@ -49,13 +53,13 @@ internal static partial class Interop
HasBirthTime = 1,
}
- [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat", SetLastError = true)]
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat2", SetLastError = true)]
internal static extern int FStat(SafeFileHandle fd, out FileStatus output);
- [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)]
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat2", SetLastError = true)]
internal static extern int Stat(string path, out FileStatus output);
- [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LStat", SetLastError = true)]
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LStat2", SetLastError = true)]
internal static extern int LStat(string path, out FileStatus output);
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs
index ec52a81bf..7f907fb6c 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs
@@ -39,6 +39,7 @@ internal partial class Interop
internal const int ERROR_NO_UNICODE_TRANSLATION = 0x459;
internal const int ERROR_NOT_FOUND = 0x490;
internal const int ERROR_BAD_IMPERSONATION_LEVEL = 0x542;
+ internal const int ERROR_NO_SYSTEM_RESOURCES = 0x5AA;
internal const int E_FILENOTFOUND = unchecked((int)0x80070002);
internal const int ERROR_TIMEOUT = 0x000005B4;
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs
index 4cce56bd0..181fb1010 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs
@@ -31,7 +31,7 @@ internal partial class Interop
internal struct WIN32_FILE_ATTRIBUTE_DATA
{
- internal int fileAttributes;
+ internal int dwFileAttributes;
internal uint ftCreationTimeLow;
internal uint ftCreationTimeHigh;
internal uint ftLastAccessTimeLow;
@@ -44,7 +44,7 @@ internal partial class Interop
internal void PopulateFrom(ref WIN32_FIND_DATA findData)
{
// Copy the information to data
- fileAttributes = (int)findData.dwFileAttributes;
+ dwFileAttributes = (int)findData.dwFileAttributes;
ftCreationTimeLow = findData.ftCreationTime.dwLowDateTime;
ftCreationTimeHigh = findData.ftCreationTime.dwHighDateTime;
ftLastAccessTimeLow = findData.ftLastAccessTime.dwLowDateTime;
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs
index 15dd58111..06030450c 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs
@@ -13,6 +13,17 @@ internal partial class Interop
/// WARNING: This method does not implicitly handle long paths. Use GetFullPathName or PathHelper.
/// </summary>
[DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
- unsafe internal static extern uint GetFullPathNameW(char* path, uint numBufferChars, char[] buffer, IntPtr mustBeZero);
+#if PROJECTN
+ internal static extern unsafe uint GetFullPathNameW(string path, uint numBufferChars, char* buffer, IntPtr mustBeZero);
+
+ // Works around https://devdiv.visualstudio.com/web/wi.aspx?pcguid=011b8bdf-6d56-4f87-be0d-0092136884d9&id=575202
+ internal static unsafe uint GetFullPathNameW(string path, uint numBufferChars, ref char buffer, IntPtr mustBeZero)
+ {
+ fixed (char* pBuffer = &buffer)
+ return GetFullPathNameW(path, numBufferChars, pBuffer, mustBeZero);
+ }
+#else
+ internal static extern uint GetFullPathNameW(string path, uint numBufferChars, ref char buffer, IntPtr mustBeZero);
+#endif
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs
index ce04078af..09e98d0c9 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs
@@ -13,6 +13,17 @@ internal partial class Interop
/// WARNING: This method does not implicitly handle long paths. Use GetFullPath/PathHelper.
/// </summary>
[DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
- internal static extern uint GetLongPathNameW(char[] lpszShortPath, char[] lpszLongPath, uint cchBuffer);
+#if PROJECTN
+ internal static extern unsafe uint GetLongPathNameW(ref char lpszShortPath, char* lpszLongPath, uint cchBuffer);
+
+ // Works around https://devdiv.visualstudio.com/web/wi.aspx?pcguid=011b8bdf-6d56-4f87-be0d-0092136884d9&id=575202
+ internal static unsafe uint GetLongPathNameW(ref char lpszShortPath, ref char lpszLongPath, uint cchBuffer)
+ {
+ fixed (char* plpszLongPath = &lpszLongPath)
+ return GetLongPathNameW(ref lpszShortPath, plpszLongPath, cchBuffer);
+ }
+#else
+ internal static extern uint GetLongPathNameW(ref char lpszShortPath, ref char lpszLongPath, uint cchBuffer);
+#endif
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs
index 00bec5d3e..ed0ada841 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs
@@ -85,10 +85,10 @@ internal static partial class Interop
char* lpString,
int cchStr);
-#if !PROJECTN
+#if !ENABLE_WINRT
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
internal static extern bool GetUserPreferredUILanguages(uint dwFlags, out uint pulNumLanguages, char [] pwszLanguagesBuffer, ref uint pcchLanguagesBuffer);
-#endif //!PROJECTN
+#endif //!ENABLE_WINRT
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetLocaleInfoEx(string lpLocaleName, uint LCType, void* lpLCData, int cchData);
diff --git a/src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MAX_PATH.cs
index a51c69bce..f7fa32669 100644
--- a/src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MAX_PATH.cs
@@ -2,13 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-namespace System.IO
+internal partial class Interop
{
- internal static partial class InternalFile
+ internal partial class Kernel32
{
- internal static bool InternalExists(String path)
- {
- throw new NotImplementedException();
- }
+ internal const int MAX_PATH = 260;
}
}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MUI.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MUI.cs
new file mode 100644
index 000000000..6ed7aa2dc
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MUI.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.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ internal const uint MUI_PREFERRED_UI_LANGUAGES = 0x10;
+
+ [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
+ internal static extern bool GetFileMUIPath(uint flags, String filePath, [Out] StringBuilder language, ref int languageLength, [Out] StringBuilder fileMuiPath, ref int fileMuiPathLength, ref Int64 enumerator);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.Registry.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.Registry.cs
new file mode 100644
index 000000000..062d1caeb
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.Registry.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct REG_TZI_FORMAT
+ {
+ internal int Bias;
+ internal int StandardBias;
+ internal int DaylightBias;
+ internal SYSTEMTIME StandardDate;
+ internal SYSTEMTIME DaylightDate;
+
+ internal REG_TZI_FORMAT(in TIME_ZONE_INFORMATION tzi)
+ {
+ Bias = tzi.Bias;
+ StandardDate = tzi.StandardDate;
+ StandardBias = tzi.StandardBias;
+ DaylightDate = tzi.DaylightDate;
+ DaylightBias = tzi.DaylightBias;
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.cs
new file mode 100644
index 000000000..05f13ac8e
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TimeZone.cs
@@ -0,0 +1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ internal struct SYSTEMTIME
+ {
+ internal ushort Year;
+ internal ushort Month;
+ internal ushort DayOfWeek;
+ internal ushort Day;
+ internal ushort Hour;
+ internal ushort Minute;
+ internal ushort Second;
+ internal ushort Milliseconds;
+
+ internal bool Equals(in SYSTEMTIME other) =>
+ Year == other.Year &&
+ Month == other.Month &&
+ DayOfWeek == other.DayOfWeek &&
+ Day == other.Day &&
+ Hour == other.Hour &&
+ Minute == other.Minute &&
+ Second == other.Second &&
+ Milliseconds == other.Milliseconds;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal unsafe struct TIME_DYNAMIC_ZONE_INFORMATION
+ {
+ internal int Bias;
+ internal fixed char StandardName[32];
+ internal SYSTEMTIME StandardDate;
+ internal int StandardBias;
+ internal fixed char DaylightName[32];
+ internal SYSTEMTIME DaylightDate;
+ internal int DaylightBias;
+ internal fixed char TimeZoneKeyName[128];
+ internal byte DynamicDaylightTimeDisabled;
+
+ internal string GetTimeZoneKeyName()
+ {
+ fixed (char* p = TimeZoneKeyName)
+ return new string(p);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal unsafe struct TIME_ZONE_INFORMATION
+ {
+ internal int Bias;
+ internal fixed char StandardName[32];
+ internal SYSTEMTIME StandardDate;
+ internal int StandardBias;
+ internal fixed char DaylightName[32];
+ internal SYSTEMTIME DaylightDate;
+ internal int DaylightBias;
+
+ internal TIME_ZONE_INFORMATION(in TIME_DYNAMIC_ZONE_INFORMATION dtzi)
+ {
+ // The start of TIME_DYNAMIC_ZONE_INFORMATION has identical layout as TIME_ZONE_INFORMATION
+ fixed (TIME_ZONE_INFORMATION* pTo = &this)
+ fixed (TIME_DYNAMIC_ZONE_INFORMATION* pFrom = &dtzi)
+ *pTo = *(TIME_ZONE_INFORMATION*)pFrom;
+ }
+
+ internal string GetStandardName()
+ {
+ fixed (char* p = StandardName)
+ return new string(p);
+ }
+
+ internal string GetDaylightName()
+ {
+ fixed (char* p = DaylightName)
+ return new string(p);
+ }
+ }
+
+ internal const uint TIME_ZONE_ID_INVALID = unchecked((uint)-1);
+
+ [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
+ internal extern static uint GetDynamicTimeZoneInformation(out TIME_DYNAMIC_ZONE_INFORMATION pTimeZoneInformation);
+
+ [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
+ internal static extern uint GetTimeZoneInformation(out TIME_ZONE_INFORMATION lpTimeZoneInformation);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeDirectoryHandle.Unix.cs b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeDirectoryHandle.Unix.cs
new file mode 100644
index 000000000..f7435eaae
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeDirectoryHandle.Unix.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeDirectoryHandle : SafeHandle
+ {
+ private SafeDirectoryHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ return Interop.Sys.CloseDir(handle) == 0;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
index f28f44fda..b284c116b 100644
--- a/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
+++ b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
@@ -83,10 +83,7 @@ namespace Microsoft.Win32.SafeHandles
private static bool DirectoryExists(string fullPath)
{
- int fileType = Interop.Sys.FileTypes.S_IFDIR;
-
Interop.Sys.FileStatus fileinfo;
- Interop.ErrorInfo errorInfo = default(Interop.ErrorInfo);
// First use stat, as we want to follow symlinks. If that fails, it could be because the symlink
// is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate
@@ -94,16 +91,10 @@ namespace Microsoft.Win32.SafeHandles
if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 &&
Interop.Sys.LStat(fullPath, out fileinfo) < 0)
{
- errorInfo = Interop.Sys.GetLastErrorInfo();
return false;
}
- // Something exists at this path. If the caller is asking for a directory, return true if it's
- // a directory and false for everything else. If the caller is asking for a file, return false for
- // a directory and true for everything else.
- return
- (fileType == Interop.Sys.FileTypes.S_IFDIR) ==
- ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR);
+ return ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR);
}
/// <summary>Opens a SafeFileHandle for a file descriptor created by a provided delegate.</summary>
diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs
index 1c30841da..4ba05409f 100644
--- a/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs
+++ b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs
@@ -2,20 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-**
-** A wrapper for find handles
-**
-**
-===========================================================*/
-
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
-using System.Runtime.ConstrainedExecution;
using Microsoft.Win32;
namespace Microsoft.Win32.SafeHandles
diff --git a/src/System.Private.CoreLib/shared/README.md b/src/System.Private.CoreLib/shared/README.md
index d55f32576..beda969ff 100644
--- a/src/System.Private.CoreLib/shared/README.md
+++ b/src/System.Private.CoreLib/shared/README.md
@@ -1,12 +1,12 @@
# System.Private.CoreLib Shared Sources
-This directory contains the shared sources for System.Private.CoreLib. These are shared between [dotnet/corert](https://github.com/dotnet/corert/tree/master/src/System.Private.CoreLib/shared) and [dotnet/coreclr](https://github.com/dotnet/coreclr/tree/master/src/mscorlib/shared).
+This directory contains the shared sources for System.Private.CoreLib. These are shared between [dotnet/corert](https://github.com/dotnet/corert/tree/master/src/System.Private.CoreLib/shared), [dotnet/coreclr](https://github.com/dotnet/coreclr/tree/master/src/mscorlib/shared) and [dotnet/corefx](https://github.com/dotnet/corefx/tree/master/src/Common/src/CoreLib).
The sources are synchronized with a mirroring tool that watches for new commits on either side and creates new pull requests (as @dotnet-bot) in the other repository.
## Conventions
-Code in the shared directory should have no code specific to CoreCLR or CoreRT. Parts of classes that need to have different implementations on different runtimes should use partial classes and &#42;.CoreRT.cs/&#42;.CoreCLR.cs files in the non shared portion. Code that is different based on platform (Windows/Unix) is fine to leave in the shared portion. Remember to follow the [style guidelines](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md).
+Code in the shared directory should have no code specific to CoreCLR, CoreRT or CoreFX. Parts of classes that need to have different implementations on different runtimes should use partial classes and &#42;.CoreRT.cs/&#42;.CoreCLR.cs/&#42;.CoreFX.cs files in the non shared portion. Code that is different based on platform (Windows/Unix) is fine to leave in the shared portion. Remember to follow the [style guidelines](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md).
## Getting clean CI and merging the mirror PRs
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index aaf6bfcf7..0b3f971b1 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -19,6 +19,7 @@
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Internal\Runtime\CompilerServices\Unsafe.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleMinusOneIsInvalid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleZeroOrMinusOneIsInvalid.cs" />
@@ -54,6 +55,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CharEnumerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CLSCompliantAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\Comparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\DictionaryEntry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\Dictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ICollection.cs" />
@@ -71,6 +73,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\KeyNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\KeyValuePair.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\NonRandomizedStringEqualityComparer.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ValueListBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\List.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\HashHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\ICollection.cs" />
@@ -131,6 +134,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarWeekRule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendricalCalculationsHelper.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CharUnicodeInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ChineseLunisolarCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.cs" />
@@ -145,6 +149,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\DaylightTime.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\DigitShapes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\EastAsianLunisolarCalendar.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GregorianCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GregorianCalendarHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GregorianCalendarTypes.cs" />
@@ -214,6 +219,10 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PinnedBufferMemoryStream.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\SeekOrigin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamHelpers.CopyValidation.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamReader.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamWriter.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\TextReader.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\TextWriter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryAccessor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStream.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStreamWrapper.cs" />
@@ -232,6 +241,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\MemberAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Memory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryDebugView.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Fast.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingMethodException.cs" />
@@ -257,6 +268,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.Fast.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlyMemory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AmbiguousMatchException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Assembly.cs" />
@@ -407,6 +419,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\TypeForwardedToAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeValueTypeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\YieldAwaitable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Cer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\ReliabilityContractAttribute.cs" />
@@ -428,12 +441,12 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\LayoutKind.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalAsAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalDirectiveException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.Fast.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\SafeBuffer.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StringBuffer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StructLayoutAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedFunctionPointerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedType.cs" />
@@ -473,8 +486,13 @@
<Compile Include="$(MSBuildThisFileDirectory)System\SerializableAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Single.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Span.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Span.Fast.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanDebugView.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Span.NonGeneric.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.BinarySearch.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Searching.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StringSpanHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />
@@ -532,6 +550,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskToApm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskSchedulerException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\ValueTask.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\Sources\IValueTaskSource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadAbortException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPriority.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadStart.cs" />
@@ -544,6 +563,10 @@
<Compile Include="$(MSBuildThisFileDirectory)System\ThreadStaticAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeoutException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZone.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.AdjustmentRule.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.StringSerializer.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.TransitionTime.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Tuple.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TupleExtensions.cs" />
@@ -561,7 +584,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\UnauthorizedAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventHandler.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\UnitySerializationHolder.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\UnitySerializationHolder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ValueTuple.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Version.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Void.cs" />
@@ -607,6 +630,37 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\TraceLogging\TraceLoggingTypeInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\TraceLogging\TypeAnalysis.cs" />
</ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\ConstantHelper.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>ConstantHelper.tt</DependentUpon>
+ </Compile>
+ <Content Include="$(MSBuildThisFileDirectory)System\Numerics\ConstantHelper.tt">
+ <Generator>TextTemplatingFileGenerator</Generator>
+ <LastGenOutput>ConstantHelper.cs</LastGenOutput>
+ </Content>
+ <None Include="$(MSBuildThisFileDirectory)System\Numerics\GenerationConfig.ttinclude" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Register.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Register.tt</DependentUpon>
+ </Compile>
+ <Content Include="$(MSBuildThisFileDirectory)System\Numerics\Register.tt">
+ <Generator>TextTemplatingFileGenerator</Generator>
+ <LastGenOutput>Register.cs</LastGenOutput>
+ </Content>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Vector.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Vector.tt</DependentUpon>
+ </Compile>
+ <Content Include="$(MSBuildThisFileDirectory)System\Numerics\Vector.tt">
+ <Generator>TextTemplatingFileGenerator</Generator>
+ <LastGenOutput>Vector.cs</LastGenOutput>
+ </Content>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Vector_Operations.cs" />
+ </ItemGroup>
<ItemGroup Condition="$(TargetsWindows)">
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs" />
@@ -632,6 +686,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.OutputDebugString.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs" />
@@ -646,11 +701,15 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Normaliz\Interop.Idna.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Normaliz\Interop.Normalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Windows.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Windows.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Windows.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
@@ -663,17 +722,19 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.StringBuffer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
</ItemGroup>
<ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' != 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeLibrary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MUI.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.Registry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.LoadString.cs" />
@@ -717,11 +778,14 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Permissions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.PosixFAdvise.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Read.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.ReadDir.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.ReadLink.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Stat.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.SysLog.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Unlink.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Write.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Unix.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeDirectoryHandle.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Debug.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
@@ -738,5 +802,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
</ItemGroup>
</Project>
diff --git a/src/System.Private.CoreLib/shared/System/AggregateException.cs b/src/System.Private.CoreLib/shared/System/AggregateException.cs
index 36b949498..99ba703a5 100644
--- a/src/System.Private.CoreLib/shared/System/AggregateException.cs
+++ b/src/System.Private.CoreLib/shared/System/AggregateException.cs
@@ -453,12 +453,12 @@ namespace System
for (int i = 0; i < m_innerExceptions.Count; i++)
{
- text.Append(Environment.NewLine);
+ text.AppendLine();
text.Append("---> ");
- text.Append(string.Format(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i));
+ text.AppendFormat(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i);
text.Append(m_innerExceptions[i].ToString());
text.Append("<---");
- text.Append(Environment.NewLine);
+ text.AppendLine();
}
return text.ToString();
diff --git a/src/System.Private.CoreLib/shared/System/ArraySegment.cs b/src/System.Private.CoreLib/shared/System/ArraySegment.cs
index d45fb0dc2..3a13595e8 100644
--- a/src/System.Private.CoreLib/shared/System/ArraySegment.cs
+++ b/src/System.Private.CoreLib/shared/System/ArraySegment.cs
@@ -192,7 +192,7 @@ namespace System
return !(a == b);
}
- public static implicit operator ArraySegment<T>(T[] array) => new ArraySegment<T>(array);
+ public static implicit operator ArraySegment<T>(T[] array) => array != null ? new ArraySegment<T>(array) : default;
#region IList<T>
T IList<T>.this[int index]
diff --git a/src/System.Private.CoreLib/shared/System/Boolean.cs b/src/System.Private.CoreLib/shared/System/Boolean.cs
index 896e5f18e..fd56082f9 100644
--- a/src/System.Private.CoreLib/shared/System/Boolean.cs
+++ b/src/System.Private.CoreLib/shared/System/Boolean.cs
@@ -100,10 +100,8 @@ namespace System
{
string s = m_value ? TrueLiteral : FalseLiteral;
- if (s.Length <= destination.Length)
+ if (s.AsSpan().TryCopyTo(destination))
{
- bool copied = s.AsReadOnlySpan().TryCopyTo(destination);
- Debug.Assert(copied);
charsWritten = s.Length;
return true;
}
@@ -183,7 +181,7 @@ namespace System
public static Boolean Parse(String value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
- return Parse(value.AsReadOnlySpan());
+ return Parse(value.AsSpan());
}
public static bool Parse(ReadOnlySpan<char> value) =>
@@ -199,19 +197,19 @@ namespace System
return false;
}
- return TryParse(value.AsReadOnlySpan(), out result);
+ return TryParse(value.AsSpan(), out result);
}
public static bool TryParse(ReadOnlySpan<char> value, out bool result)
{
- ReadOnlySpan<char> trueSpan = TrueLiteral.AsReadOnlySpan();
+ ReadOnlySpan<char> trueSpan = TrueLiteral.AsSpan();
if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = true;
return true;
}
- ReadOnlySpan<char> falseSpan = FalseLiteral.AsReadOnlySpan();
+ ReadOnlySpan<char> falseSpan = FalseLiteral.AsSpan();
if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = false;
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs b/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs
index 948274414..b2d0dbd32 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs
@@ -6,7 +6,7 @@ using System.Diagnostics.Tracing;
namespace System.Buffers
{
- [EventSource(Name = "System.Buffers.ArrayPoolEventSource")]
+ [EventSource(Guid = "0866B2B8-5CEF-5DB9-2612-0C0FFD814A44", Name = "System.Buffers.ArrayPoolEventSource")]
internal sealed class ArrayPoolEventSource : EventSource
{
internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource();
@@ -22,6 +22,9 @@ namespace System.Buffers
PoolExhausted
}
+ // The ArrayPoolEventSource GUID is {0866b2b8-5cef-5db9-2612-0c0ffd814a44}
+ private ArrayPoolEventSource() : base(new Guid(0x0866b2b8, 0x5cef, 0x5db9, 0x26, 0x12, 0x0c, 0x0f, 0xfd, 0x81, 0x4a, 0x44), "System.Buffers.ArrayPoolEventSource") { }
+
/// <summary>
/// Event for when a buffer is rented. This is invoked once for every successful call to Rent,
/// regardless of whether a buffer is allocated or a buffer is taken from the pool. In a
@@ -36,12 +39,16 @@ namespace System.Buffers
EventData* payload = stackalloc EventData[4];
payload[0].Size = sizeof(int);
payload[0].DataPointer = ((IntPtr)(&bufferId));
+ payload[0].Reserved = 0;
payload[1].Size = sizeof(int);
payload[1].DataPointer = ((IntPtr)(&bufferSize));
+ payload[1].Reserved = 0;
payload[2].Size = sizeof(int);
payload[2].DataPointer = ((IntPtr)(&poolId));
+ payload[2].Reserved = 0;
payload[3].Size = sizeof(int);
payload[3].DataPointer = ((IntPtr)(&bucketId));
+ payload[3].Reserved = 0;
WriteEventCore(1, 4, payload);
}
@@ -56,14 +63,19 @@ namespace System.Buffers
EventData* payload = stackalloc EventData[5];
payload[0].Size = sizeof(int);
payload[0].DataPointer = ((IntPtr)(&bufferId));
+ payload[0].Reserved = 0;
payload[1].Size = sizeof(int);
payload[1].DataPointer = ((IntPtr)(&bufferSize));
+ payload[1].Reserved = 0;
payload[2].Size = sizeof(int);
payload[2].DataPointer = ((IntPtr)(&poolId));
+ payload[2].Reserved = 0;
payload[3].Size = sizeof(int);
payload[3].DataPointer = ((IntPtr)(&bucketId));
+ payload[3].Reserved = 0;
payload[4].Size = sizeof(BufferAllocatedReason);
payload[4].DataPointer = ((IntPtr)(&reason));
+ payload[4].Reserved = 0;
WriteEventCore(2, 5, payload);
}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs b/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
index 8d71fc614..6ac508859 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
@@ -7,9 +7,20 @@ using System.Runtime.CompilerServices;
namespace System.Buffers
{
+ /// <summary>
+ /// Provides a mechanism for manual lifetime management.
+ /// </summary>
public interface IRetainable
{
+ /// <summary>
+ /// Call this method to indicate that the IRetainable object is in use.
+ /// Do not dispose until Release is called.
+ /// </summary>
void Retain();
+ /// <summary>
+ /// Call this method to indicate that the IRetainable object is no longer in use.
+ /// The object can now be disposed.
+ /// </summary>
bool Release();
}
} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs b/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
index 60592144a..754403862 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
@@ -7,52 +7,58 @@ using System.Runtime.InteropServices;
namespace System.Buffers
{
+ /// <summary>
+ /// A handle for the memory.
+ /// </summary>
public unsafe struct MemoryHandle : IDisposable
{
- private IRetainable _owner;
+ private IRetainable _retainable;
private void* _pointer;
private GCHandle _handle;
+ /// <summary>
+ /// Creates a new memory handle for the memory.
+ /// </summary>
+ /// <param name="retainable">reference to manually managed object</param>
+ /// <param name="pointer">pointer to memory, or null if a pointer was not provided when the handle was created</param>
+ /// <param name="handle">handle used to pin array buffers</param>
[CLSCompliant(false)]
- public MemoryHandle(IRetainable owner, void* pointer = null, GCHandle handle = default(GCHandle))
+ public MemoryHandle(IRetainable retainable, void* pointer = null, GCHandle handle = default(GCHandle))
{
- _owner = owner;
+ _retainable = retainable;
_pointer = pointer;
_handle = handle;
}
- internal void AddOffset(int offset)
- {
- if (_pointer == null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.pointer);
- }
- else
- {
- _pointer = (void*)((byte*)_pointer + offset);
- }
- }
-
+ /// <summary>
+ /// Returns the pointer to memory, or null if a pointer was not provided when the handle was created.
+ /// </summary>
[CLSCompliant(false)]
public void* Pointer => _pointer;
+ /// <summary>
+ /// Returns false if the pointer to memory is null.
+ /// </summary>
public bool HasPointer => _pointer != null;
- public void Dispose()
- {
- if (_handle.IsAllocated)
+ /// <summary>
+ /// Frees the pinned handle and releases IRetainable.
+ /// </summary>
+ public void Dispose()
+ {
+ if (_handle.IsAllocated)
{
_handle.Free();
}
- if (_owner != null)
+ if (_retainable != null)
{
- _owner.Release();
- _owner = null;
+ _retainable.Release();
+ _retainable = null;
}
- _pointer = null;
+ _pointer = null;
}
-
+
}
} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs b/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
index 116767073..eade1feff 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
@@ -7,46 +7,88 @@ using System.Runtime.CompilerServices;
namespace System.Buffers
{
+ /// <summary>
+ /// Owner of Memory<typeparamref name="T"/> that provides appropriate lifetime management mechanisms for it.
+ /// </summary>
public abstract class OwnedMemory<T> : IDisposable, IRetainable
{
+ /// <summary>
+ /// The number of items in the Memory<typeparamref name="T"/>.
+ /// </summary>
public abstract int Length { get; }
+ /// <summary>
+ /// Returns a span wrapping the underlying memory.
+ /// </summary>
public abstract Span<T> Span { get; }
+ /// <summary>
+ /// Returns a Memory<typeparamref name="T"/> if the underlying memory has not been freed.
+ /// </summary>
+ /// <exception cref="System.ObjectDisposedException">
+ /// Thrown when the underlying memory has already been disposed.
+ /// </exception>
public Memory<T> Memory
{
- get
+ get
{
- if (IsDisposed)
+ if (IsDisposed)
{
- ThrowHelper.ThrowObjectDisposedException(nameof(OwnedMemory<T>), ExceptionResource.Memory_ThrowIfDisposed);
+ ThrowHelper.ThrowObjectDisposedException_MemoryDisposed();
}
return new Memory<T>(owner: this, 0, Length);
}
}
- public abstract MemoryHandle Pin();
+ /// <summary>
+ /// Returns a handle for the array that has been pinned and hence its address can be taken
+ /// </summary>
+ public abstract MemoryHandle Pin(int byteOffset = 0);
+ /// <summary>
+ /// Returns an array segment.
+ /// </summary>
protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment);
+ /// <summary>
+ /// Implements IDisposable.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">
+ /// Throw when there are still retained references to the memory
+ /// </exception>
public void Dispose()
{
- if (IsRetained)
+ if (IsRetained)
{
- ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Memory_OutstandingReferences);
+ ThrowHelper.ThrowInvalidOperationException_OutstandingReferences();
}
Dispose(true);
GC.SuppressFinalize(this);
}
+ /// <summary>
+ /// Clean up of any leftover managed and unmanaged resources.
+ /// </summary>
protected abstract void Dispose(bool disposing);
+ /// <summary>
+ /// Return true if someone is holding a reference to the memory.
+ /// </summary>
protected abstract bool IsRetained { get; }
+ /// <summary>
+ /// Return true if the underlying memory has been freed.
+ /// </summary>
public abstract bool IsDisposed { get; }
+ /// <summary>
+ /// Implements IRetainable. Prevent accidental disposal of the memory.
+ /// </summary>
public abstract void Retain();
+ /// <summary>
+ /// Implements IRetainable. The memory can now be diposed.
+ /// </summary>
public abstract bool Release();
}
diff --git a/src/System.Private.CoreLib/shared/System/Char.cs b/src/System.Private.CoreLib/shared/System/Char.cs
index 6059830fb..d3ed1f5b6 100644
--- a/src/System.Private.CoreLib/shared/System/Char.cs
+++ b/src/System.Private.CoreLib/shared/System/Char.cs
@@ -401,14 +401,14 @@ namespace System
//
public static char ToUpper(char c)
{
- return ToUpper(c, CultureInfo.CurrentCulture);
+ return CultureInfo.CurrentCulture.TextInfo.ToUpper(c);
}
// Converts a character to upper-case for invariant culture.
public static char ToUpperInvariant(char c)
{
- return ToUpper(c, CultureInfo.InvariantCulture);
+ return CultureInfo.InvariantCulture.TextInfo.ToUpper(c);
}
@@ -432,14 +432,14 @@ namespace System
// Converts a character to lower-case for the default culture.
public static char ToLower(char c)
{
- return ToLower(c, CultureInfo.CurrentCulture);
+ return CultureInfo.CurrentCulture.TextInfo.ToLower(c);
}
// Converts a character to lower-case for invariant culture.
public static char ToLowerInvariant(char c)
{
- return ToLower(c, CultureInfo.InvariantCulture);
+ return CultureInfo.InvariantCulture.TextInfo.ToLower(c);
}
@@ -853,7 +853,7 @@ namespace System
{
return (GetLatin1UnicodeCategory(c));
}
- return CharUnicodeInfo.InternalGetUnicodeCategory(c);
+ return CharUnicodeInfo.GetUnicodeCategory((int)c);
}
public static UnicodeCategory GetUnicodeCategory(String s, int index)
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Comparer.cs b/src/System.Private.CoreLib/shared/System/Collections/Comparer.cs
new file mode 100644
index 000000000..6fcedc09a
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Collections/Comparer.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Purpose: Default IComparer implementation.
+**
+===========================================================*/
+
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace System.Collections
+{
+ [Serializable]
+ [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public sealed class Comparer : IComparer, ISerializable
+ {
+ private CompareInfo _compareInfo;
+
+ public static readonly Comparer Default = new Comparer(CultureInfo.CurrentCulture);
+ public static readonly Comparer DefaultInvariant = new Comparer(CultureInfo.InvariantCulture);
+
+ public Comparer(CultureInfo culture)
+ {
+ if (culture == null)
+ throw new ArgumentNullException(nameof(culture));
+
+ _compareInfo = culture.CompareInfo;
+ }
+
+ private Comparer(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ _compareInfo = (CompareInfo)info.GetValue("CompareInfo", typeof(CompareInfo));
+ }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ info.AddValue("CompareInfo", _compareInfo);
+ }
+
+ // Compares two Objects by calling CompareTo.
+ // If a == b, 0 is returned.
+ // If a implements IComparable, a.CompareTo(b) is returned.
+ // If a doesn't implement IComparable and b does, -(b.CompareTo(a)) is returned.
+ // Otherwise an exception is thrown.
+ //
+ public int Compare(Object a, Object b)
+ {
+ if (a == b) return 0;
+ if (a == null) return -1;
+ if (b == null) return 1;
+
+ string sa = a as string;
+ string sb = b as string;
+ if (sa != null && sb != null)
+ return _compareInfo.Compare(sa, sb);
+
+ IComparable ia = a as IComparable;
+ if (ia != null)
+ return ia.CompareTo(b);
+
+ IComparable ib = b as IComparable;
+ if (ib != null)
+ return -ib.CompareTo(a);
+
+ throw new ArgumentException(SR.Argument_ImplementIComparable);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
index ab7f0dba8..8fd8d91f0 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
@@ -77,11 +77,15 @@ namespace System.Collections.Generic
{
if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
if (capacity > 0) Initialize(capacity);
- _comparer = comparer ?? EqualityComparer<TKey>.Default;
+ if (comparer != EqualityComparer<TKey>.Default)
+ {
+ _comparer = comparer;
+ }
#if !MONO
- if (_comparer == EqualityComparer<string>.Default)
+ if (typeof(TKey) == typeof(string) && _comparer == null)
{
+ // To start, move off default comparer for string which is randomised
_comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
}
#endif
@@ -150,7 +154,7 @@ namespace System.Collections.Generic
{
get
{
- return _comparer;
+ return (_comparer == null || _comparer is NonRandomizedStringEqualityComparer) ? EqualityComparer<TKey>.Default : _comparer;
}
}
@@ -266,11 +270,7 @@ namespace System.Collections.Generic
int count = _count;
if (count > 0)
{
- int[] buckets = _buckets;
- for (int i = 0; i < buckets.Length; i++)
- {
- buckets[i] = -1;
- }
+ Array.Clear(_buckets, 0, _buckets.Length);
_count = 0;
_freeList = -1;
@@ -296,10 +296,9 @@ namespace System.Collections.Generic
}
else
{
- EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
for (int i = 0; i < _count; i++)
{
- if (_entries[i].hashCode >= 0 && c.Equals(_entries[i].value, value)) return true;
+ if (_entries[i].hashCode >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, value)) return true;
}
}
return false;
@@ -351,7 +350,7 @@ namespace System.Collections.Generic
}
info.AddValue(VersionName, _version);
- info.AddValue(ComparerName, _comparer, typeof(IEqualityComparer<TKey>));
+ info.AddValue(ComparerName, _comparer ?? EqualityComparer<TKey>.Default, typeof(IEqualityComparer<TKey>));
info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); // This is the length of the bucket array
if (_buckets != null)
@@ -369,29 +368,61 @@ namespace System.Collections.Generic
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
- if (_buckets != null)
+ int i = -1;
+ int[] buckets = _buckets;
+ Entry[] entries = _entries;
+ if (buckets != null)
{
- int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
- for (int i = _buckets[hashCode % _buckets.Length]; i >= 0; i = _entries[i].next)
+ IEqualityComparer<TKey> comparer = _comparer;
+ if (comparer == null)
+ {
+ int hashCode = key.GetHashCode() & 0x7FFFFFFF;
+ // Value in _buckets is 1-based
+ i = buckets[hashCode % buckets.Length] - 1;
+ do
+ {
+ // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+ // Test in if to drop range check for following array access
+ if ((uint)i >= (uint)entries.Length || (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key)))
+ {
+ break;
+ }
+
+ i = entries[i].next;
+ } while (true);
+ }
+ else
{
- if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i;
+ int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+ // Value in _buckets is 1-based
+ i = buckets[hashCode % buckets.Length] - 1;
+ do
+ {
+ // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+ // Test in if to drop range check for following array access
+ if ((uint)i >= (uint)entries.Length ||
+ (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)))
+ {
+ break;
+ }
+
+ i = entries[i].next;
+ } while (true);
}
}
- return -1;
+
+ return i;
}
- private void Initialize(int capacity)
+ private int Initialize(int capacity)
{
int size = HashHelpers.GetPrime(capacity);
- int[] buckets = new int[size];
- for (int i = 0; i < buckets.Length; i++)
- {
- buckets[i] = -1;
- }
_freeList = -1;
- _buckets = buckets;
+ _buckets = new int[size];
_entries = new Entry[size];
+
+ return size;
}
private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
@@ -401,65 +432,135 @@ namespace System.Collections.Generic
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
- if (_buckets == null) Initialize(0);
- int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
- int targetBucket = hashCode % _buckets.Length;
+ if (_buckets == null)
+ {
+ Initialize(0);
+ }
+
+ Entry[] entries = _entries;
+ IEqualityComparer<TKey> comparer = _comparer;
+
+ int hashCode = ((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)) & 0x7FFFFFFF;
+
int collisionCount = 0;
+ ref int bucket = ref _buckets[hashCode % _buckets.Length];
+ // Value in _buckets is 1-based
+ int i = bucket - 1;
- for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
+ if (comparer == null)
{
- if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key))
+ do
{
- if (behavior == InsertionBehavior.OverwriteExisting)
+ // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+ // Test uint in if rather than loop condition to drop range check for following array access
+ if ((uint)i >= (uint)entries.Length)
{
- _entries[i].value = value;
- _version++;
- return true;
+ break;
}
- if (behavior == InsertionBehavior.ThrowOnExisting)
+ if (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key))
{
- ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+ if (behavior == InsertionBehavior.OverwriteExisting)
+ {
+ entries[i].value = value;
+ _version++;
+ return true;
+ }
+
+ if (behavior == InsertionBehavior.ThrowOnExisting)
+ {
+ ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+ }
+
+ return false;
}
- return false;
- }
- collisionCount++;
+ i = entries[i].next;
+ collisionCount++;
+ } while (true);
}
+ else
+ {
+ do
+ {
+ // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+ // Test uint in if rather than loop condition to drop range check for following array access
+ if ((uint)i >= (uint)entries.Length)
+ {
+ break;
+ }
+ if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+ {
+ if (behavior == InsertionBehavior.OverwriteExisting)
+ {
+ entries[i].value = value;
+ _version++;
+ return true;
+ }
+
+ if (behavior == InsertionBehavior.ThrowOnExisting)
+ {
+ ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+ }
+
+ return false;
+ }
+
+ i = entries[i].next;
+ collisionCount++;
+ } while (true);
+
+ }
+
+ // Can be improved with "Ref Local Reassignment"
+ // https://github.com/dotnet/csharplang/blob/master/proposals/ref-local-reassignment.md
+ bool resized = false;
+ bool updateFreeList = false;
int index;
if (_freeCount > 0)
{
index = _freeList;
- _freeList = _entries[index].next;
+ updateFreeList = true;
_freeCount--;
}
else
{
- if (_count == _entries.Length)
+ int count = _count;
+ if (count == entries.Length)
{
Resize();
- targetBucket = hashCode % _buckets.Length;
+ resized = true;
}
- index = _count;
- _count++;
+ index = count;
+ _count = count + 1;
+ entries = _entries;
}
- _entries[index].hashCode = hashCode;
- _entries[index].next = _buckets[targetBucket];
- _entries[index].key = key;
- _entries[index].value = value;
- _buckets[targetBucket] = index;
+ ref int targetBucket = ref resized ? ref _buckets[hashCode % _buckets.Length] : ref bucket;
+ ref Entry entry = ref entries[index];
+
+ if (updateFreeList)
+ {
+ _freeList = entry.next;
+ }
+ entry.hashCode = hashCode;
+ // Value in _buckets is 1-based
+ entry.next = targetBucket - 1;
+ entry.key = key;
+ entry.value = value;
+ // Value in _buckets is 1-based
+ targetBucket = index + 1;
_version++;
#if !MONO
- // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
- // i.e. EqualityComparer<string>.Default.
-
- if (collisionCount > HashHelpers.HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer)
+ // Value types never rehash
+ if (default(TKey) == null && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer)
{
- _comparer = (IEqualityComparer<TKey>)EqualityComparer<string>.Default;
- Resize(_entries.Length, true);
+ // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
+ // i.e. EqualityComparer<string>.Default.
+ _comparer = null;
+ Resize(entries.Length, true);
}
#endif
@@ -519,25 +620,24 @@ namespace System.Collections.Generic
private void Resize(int newSize, bool forceNewHashCodes)
{
+ // Value types never rehash
+ Debug.Assert(!forceNewHashCodes || default(TKey) == null);
Debug.Assert(newSize >= _entries.Length);
int[] buckets = new int[newSize];
- for (int i = 0; i < buckets.Length; i++)
- {
- buckets[i] = -1;
- }
Entry[] entries = new Entry[newSize];
int count = _count;
Array.Copy(_entries, 0, entries, 0, count);
- if (forceNewHashCodes)
+ if (default(TKey) == null && forceNewHashCodes)
{
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode != -1)
+ if (entries[i].hashCode >= 0)
{
- entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF);
+ Debug.Assert(_comparer == null);
+ entries[i].hashCode = (entries[i].key.GetHashCode() & 0x7FFFFFFF);
}
}
}
@@ -547,8 +647,10 @@ namespace System.Collections.Generic
if (entries[i].hashCode >= 0)
{
int bucket = entries[i].hashCode % newSize;
- entries[i].next = buckets[bucket];
- buckets[bucket] = i;
+ // Value in _buckets is 1-based
+ entries[i].next = buckets[bucket] - 1;
+ // Value in _buckets is 1-based
+ buckets[bucket] = i + 1;
}
}
@@ -568,19 +670,21 @@ namespace System.Collections.Generic
if (_buckets != null)
{
- int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+ int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
int bucket = hashCode % _buckets.Length;
int last = -1;
- int i = _buckets[bucket];
+ // Value in _buckets is 1-based
+ int i = _buckets[bucket] - 1;
while (i >= 0)
{
ref Entry entry = ref _entries[i];
- if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key))
+ if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
{
if (last < 0)
{
- _buckets[bucket] = entry.next;
+ // Value in _buckets is 1-based
+ _buckets[bucket] = entry.next + 1;
}
else
{
@@ -622,19 +726,21 @@ namespace System.Collections.Generic
if (_buckets != null)
{
- int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+ int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
int bucket = hashCode % _buckets.Length;
int last = -1;
- int i = _buckets[bucket];
+ // Value in _buckets is 1-based
+ int i = _buckets[bucket] - 1;
while (i >= 0)
{
ref Entry entry = ref _entries[i];
- if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key))
+ if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
{
if (last < 0)
{
- _buckets[bucket] = entry.next;
+ // Value in _buckets is 1-based
+ _buckets[bucket] = entry.next + 1;
}
else
{
@@ -768,6 +874,80 @@ namespace System.Collections.Generic
return new Enumerator(this, Enumerator.KeyValuePair);
}
+ /// <summary>
+ /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
+ /// </summary>
+ public int EnsureCapacity(int capacity)
+ {
+ if (capacity < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+ int currentCapacity = _entries == null ? 0 : _entries.Length;
+ if (currentCapacity >= capacity)
+ return currentCapacity;
+ if (_buckets == null)
+ return Initialize(capacity);
+ int newSize = HashHelpers.GetPrime(capacity);
+ Resize(newSize, forceNewHashCodes: false);
+ return newSize;
+ }
+
+ /// <summary>
+ /// Sets the capacity of this dictionary to what it would be if it had been originally initialized with all its entries
+ ///
+ /// This method can be used to minimize the memory overhead
+ /// once it is known that no new elements will be added.
+ ///
+ /// To allocate minimum size storage array, execute the following statements:
+ ///
+ /// dictionary.Clear();
+ /// dictionary.TrimExcess();
+ /// </summary>
+ public void TrimExcess()
+ {
+ TrimExcess(Count);
+ }
+
+ /// <summary>
+ /// Sets the capacity of this dictionary to hold up 'capacity' entries without any further expansion of its backing storage
+ ///
+ /// This method can be used to minimize the memory overhead
+ /// once it is known that no new elements will be added.
+ /// </summary>
+ public void TrimExcess(int capacity)
+ {
+ if (capacity < Count)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+ int newSize = HashHelpers.GetPrime(capacity);
+
+ Entry[] oldEntries = _entries;
+ int currentCapacity = oldEntries == null ? 0 : oldEntries.Length;
+ if (newSize >= currentCapacity)
+ return;
+
+ int oldCount = _count;
+ Initialize(newSize);
+ Entry[] entries = _entries;
+ int[] buckets = _buckets;
+ int count = 0;
+ for (int i = 0; i < oldCount; i++)
+ {
+ int hashCode = oldEntries[i].hashCode;
+ if (hashCode >= 0)
+ {
+ ref Entry entry = ref entries[count];
+ entry = oldEntries[i];
+ int bucket = hashCode % newSize;
+ // Value in _buckets is 1-based
+ entry.next = buckets[bucket] - 1;
+ // Value in _buckets is 1-based
+ buckets[bucket] = count + 1;
+ count++;
+ }
+ }
+ _count = count;
+ _freeCount = 0;
+ }
+
bool ICollection.IsSynchronized
{
get { return false; }
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs
index ef44fefc8..72f62c2b9 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs
@@ -11,10 +11,10 @@ namespace System.Collections.Generic
// keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using
// randomized string hashing.
[Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob
-#if CORECLR
- internal
-#else
+#if CORERT
public
+#else
+ internal
#endif
sealed class NonRandomizedStringEqualityComparer : EqualityComparer<string>, ISerializable
{
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/ValueListBuilder.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/ValueListBuilder.cs
new file mode 100644
index 000000000..72da4a9e1
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/ValueListBuilder.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+ internal ref partial struct ValueListBuilder<T>
+ {
+ private Span<T> _span;
+ private T[] _arrayFromPool;
+ private int _pos;
+
+ public ValueListBuilder(Span<T> initialSpan)
+ {
+ _span = initialSpan;
+ _arrayFromPool = null;
+ _pos = 0;
+ }
+
+ public int Length => _pos;
+
+ public ref T this[int index]
+ {
+ get
+ {
+ Debug.Assert(index < _pos);
+ return ref _span[index];
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Append(T item)
+ {
+ int pos = _pos;
+ if (pos >= _span.Length)
+ Grow();
+
+ _span[pos] = item;
+ _pos = pos + 1;
+ }
+
+ public ReadOnlySpan<T> AsSpan()
+ {
+ return _span.Slice(0, _pos);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Dispose()
+ {
+ if (_arrayFromPool != null)
+ {
+ ArrayPool<T>.Shared.Return(_arrayFromPool);
+ _arrayFromPool = null;
+ }
+ }
+
+ private void Grow()
+ {
+ T[] array = ArrayPool<T>.Shared.Rent(_span.Length * 2);
+
+ bool success = _span.TryCopyTo(array);
+ Debug.Assert(success);
+
+ T[] toReturn = _arrayFromPool;
+ _span = _arrayFromPool = array;
+ if (toReturn != null)
+ {
+ ArrayPool<T>.Shared.Return(toReturn);
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Convert.cs b/src/System.Private.CoreLib/shared/System/Convert.cs
index 488ea7733..756bf17fc 100644
--- a/src/System.Private.CoreLib/shared/System/Convert.cs
+++ b/src/System.Private.CoreLib/shared/System/Convert.cs
@@ -2198,7 +2198,7 @@ namespace System
return 0;
}
- int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
+ int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
if (r < Byte.MinValue || r > Byte.MaxValue)
ThrowByteOverflowException();
return (byte)r;
@@ -2221,7 +2221,7 @@ namespace System
return 0;
}
- int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1);
+ int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1);
if (fromBase != 10 && r <= Byte.MaxValue)
return (sbyte)r;
@@ -2246,7 +2246,7 @@ namespace System
return 0;
}
- int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2);
+ int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2);
if (fromBase != 10 && r <= UInt16.MaxValue)
return (short)r;
@@ -2272,7 +2272,7 @@ namespace System
return 0;
}
- int r = ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
+ int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
if (r < UInt16.MinValue || r > UInt16.MaxValue)
ThrowUInt16OverflowException();
return (ushort)r;
@@ -2289,7 +2289,7 @@ namespace System
throw new ArgumentException(SR.Arg_InvalidBase);
}
return value != null ?
- ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight) :
+ ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight) :
0;
}
@@ -2305,7 +2305,7 @@ namespace System
throw new ArgumentException(SR.Arg_InvalidBase);
}
return value != null ?
- (uint)ParseNumbers.StringToInt(value.AsReadOnlySpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) :
+ (uint)ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) :
0;
}
@@ -2320,7 +2320,7 @@ namespace System
throw new ArgumentException(SR.Arg_InvalidBase);
}
return value != null ?
- ParseNumbers.StringToLong(value.AsReadOnlySpan(), fromBase, ParseNumbers.IsTight) :
+ ParseNumbers.StringToLong(value.AsSpan(), fromBase, ParseNumbers.IsTight) :
0;
}
@@ -2336,7 +2336,7 @@ namespace System
throw new ArgumentException(SR.Arg_InvalidBase);
}
return value != null ?
- (ulong)ParseNumbers.StringToLong(value.AsReadOnlySpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) :
+ (ulong)ParseNumbers.StringToLong(value.AsSpan(), fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight) :
0;
}
@@ -2653,7 +2653,7 @@ namespace System
throw new ArgumentNullException(nameof(s));
}
- return TryFromBase64Chars(s.AsReadOnlySpan(), bytes, out bytesWritten);
+ return TryFromBase64Chars(s.AsSpan(), bytes, out bytesWritten);
}
public static unsafe bool TryFromBase64Chars(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.Unix.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.Unix.cs
index 495f2f713..627ea4ab7 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.Unix.cs
@@ -10,7 +10,7 @@ namespace System.Diagnostics
{
private static readonly bool s_shouldWriteToStdErr = Environment.GetEnvironmentVariable("COMPlus_DebugWriteToStdErr") == "1";
- private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+ private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
{
if (Debugger.IsAttached)
{
@@ -21,8 +21,20 @@ namespace System.Diagnostics
// In Core, we do not show a dialog.
// Fail in order to avoid anyone catching an exception and masking
// an assert failure.
- var ex = new DebugAssertException(message, detailMessage, stackTrace);
- Environment.FailFast(ex.Message, ex);
+ DebugAssertException ex;
+ if (message == String.Empty)
+ {
+ ex = new DebugAssertException(stackTrace);
+ }
+ else if (detailMessage == String.Empty)
+ {
+ ex = new DebugAssertException(message, stackTrace);
+ }
+ else
+ {
+ ex = new DebugAssertException(message, detailMessage, stackTrace);
+ }
+ Environment.FailFast(ex.Message, ex, errorSource);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs
index 4efb4432a..45f31928d 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs
@@ -4,6 +4,8 @@
// Do not remove this, it is needed to retain calls to these conditional methods in release builds
#define DEBUG
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
#if MONO
namespace System.Diagnostics.Private
@@ -98,18 +100,34 @@ namespace System.Diagnostics
if (!condition)
{
string stackTrace;
-
try
{
- stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
+ stackTrace = new StackTrace(0, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
}
catch
{
stackTrace = "";
}
+ WriteLine(FormatAssert(stackTrace, message, detailMessage));
+ s_ShowDialog(stackTrace, message, detailMessage, "Assertion Failed");
+ }
+ }
+ internal static void ContractFailure(bool condition, string message, string detailMessage, string failureKindMessage)
+ {
+ if (!condition)
+ {
+ string stackTrace;
+ try
+ {
+ stackTrace = new StackTrace(2, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
+ }
+ catch
+ {
+ stackTrace = "";
+ }
WriteLine(FormatAssert(stackTrace, message, detailMessage));
- s_ShowAssertDialog(stackTrace, message, detailMessage);
+ s_ShowDialog(stackTrace, message, detailMessage, SR.GetResourceString(failureKindMessage));
}
}
@@ -315,14 +333,25 @@ namespace System.Diagnostics
private sealed class DebugAssertException : Exception
{
+ internal DebugAssertException(string stackTrace) :
+ base(Environment.NewLine + stackTrace)
+ {
+ }
+
+ internal DebugAssertException(string message, string stackTrace) :
+ base(message + Environment.NewLine + Environment.NewLine + stackTrace)
+ {
+ }
+
internal DebugAssertException(string message, string detailMessage, string stackTrace) :
- base(message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace)
+ base(message + Environment.NewLine + detailMessage + Environment.NewLine + Environment.NewLine + stackTrace)
{
}
}
// internal and not readonly so that the tests can swap this out.
- internal static Action<string, string, string> s_ShowAssertDialog = ShowAssertDialog;
+ internal static Action<string, string, string, string> s_ShowDialog = ShowDialog;
+
internal static Action<string> s_WriteCore = WriteCore;
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
index 64c249176..fafdd7c6d 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
@@ -182,7 +182,7 @@ namespace System.Diagnostics.Tracing
//
//
- // check if the object has been allready disposed
+ // check if the object has been already disposed
//
if (m_disposed) return;
@@ -259,12 +259,6 @@ namespace System.Diagnostics.Tracing
m_anyKeywordMask = anyKeyword;
m_allKeywordMask = allKeyword;
- // ES_SESSION_INFO is a marker for additional places we #ifdeffed out to remove
- // references to EnumerateTraceGuidsEx. This symbol is actually not used because
- // today we use FEATURE_ACTIVITYSAMPLING to determine if this code is there or not.
- // However we put it in the #if so that we don't lose the fact that this feature
- // switch is at least partially independent of FEATURE_ACTIVITYSAMPLING
-
List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions();
foreach (var session in sessionsChanged)
{
@@ -567,7 +561,7 @@ namespace System.Diagnostics.Tracing
if (filterData == null)
{
#if (!ES_BUILD_PCL && !ES_BUILD_PN && PLATFORM_WINDOWS)
- string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}";
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey;
else
@@ -964,6 +958,8 @@ namespace System.Diagnostics.Tracing
List<int> refObjPosition = new List<int>(s_etwAPIMaxRefObjCount);
List<object> dataRefObj = new List<object>(s_etwAPIMaxRefObjCount);
EventData* userData = stackalloc EventData[2 * argCount];
+ for (int i = 0; i < 2 * argCount; i++)
+ userData[i] = default(EventData);
EventData* userDataPtr = (EventData*)userData;
byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument
byte* currentBuffer = dataBuffer;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
index f6d489984..40d39ced7 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -4,10 +4,6 @@
// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
// It is available from http://www.codeplex.com/hyperAddin
-#if PLATFORM_WINDOWS && !ES_BUILD_STANDALONE && !CORECLR && !ES_BUILD_PN
-#define FEATURE_ACTIVITYSAMPLING
-#endif // !ES_BUILD_STANDALONE
-
#if ES_BUILD_STANDALONE
#define FEATURE_MANAGED_ETW_CHANNELS
// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
@@ -24,13 +20,13 @@
//
// Conceptually and EventSouce is something takes event logging data from the source methods
// To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
-// KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
+// KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListener Which
// we will call the EtwEventListener, that forwards commands from ETW to EventSources and
// listeners to the EventSources and forwards on those events to ETW. THus the model should
// be that you DON'T NEED ETW.
//
// Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
-// to it directly, but this can be VIEWED AS AN OPTIMIATION.
+// to it directly, but this can be VIEWED AS AN OPTIMIZATION.
//
// Basic Event Data Flow:
//
@@ -170,9 +166,6 @@
//
using System;
using System.Runtime.CompilerServices;
-#if FEATURE_ACTIVITYSAMPLING
-using System.Collections.Concurrent;
-#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
@@ -308,83 +301,7 @@ namespace System.Diagnostics.Tracing
if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
return false;
-#if !FEATURE_ACTIVITYSAMPLING
-
return true;
-
-#else // FEATURE_ACTIVITYSAMPLING
-
- return true;
-
-#if OPTIMIZE_IS_ENABLED
- //================================================================================
- // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
- // in case activity tracing/sampling is enabled. The added complexity of this
- // code however weighs against having it "on" until we know it's really needed.
- // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
- //================================================================================
-
- // At this point we believe the event is enabled, however we now need to check
- // if we filter because of activity
-
- // Optimization, all activity filters also register a delegate here, so if there
- // is no delegate, we know there are no activity filters, which means that there
- // is no additional filtering, which means that we can return true immediately.
- if (s_activityDying == null)
- return true;
-
- // if there's at least one legacy ETW listener we can't filter this
- if (m_legacySessions != null && m_legacySessions.Count > 0)
- return true;
-
- // if any event ID that triggers a new activity, or "transfers" activities
- // is covered by 'keywords' we can't filter this
- if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
- return true;
-
- // See if all listeners have activity filters that would block the event.
- for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
- {
- EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
- if (etwSession == null)
- continue;
-
- ActivityFilter activityFilter = etwSession.m_activityFilter;
- if (activityFilter == null ||
- ActivityFilter.GetFilter(activityFilter, this) == null)
- {
- // No activity filter for ETW, if event is active for ETW, we can't filter.
- for (int i = 0; i < m_eventData.Length; i++)
- if (m_eventData[i].EnabledForETW)
- return true;
- }
- else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
- return true;
- }
-
- // for regular event listeners
- var curDispatcher = m_Dispatchers;
- while (curDispatcher != null)
- {
- ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
- if (activityFilter == null)
- {
- // See if any event is enabled.
- for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
- if (curDispatcher.m_EventEnabled[i])
- return true;
- }
- else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
- return true;
- curDispatcher = curDispatcher.m_Next;
- }
-
- // Every listener has an activity filter that is blocking writing the event,
- // thus the event is not enabled.
- return false;
-#endif // OPTIMIZE_IS_ENABLED
-
-#endif // FEATURE_ACTIVITYSAMPLING
}
/// <summary>
@@ -739,7 +656,7 @@ namespace System.Diagnostics.Tracing
foreach (var parameter in m_eventData[i].Parameters)
{
// Write parameter type.
- WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)Type.GetTypeCode(parameter.ParameterType));
+ WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)GetTypeCodeExtended(parameter.ParameterType));
// Write parameter name.
string parameterName = parameter.Name;
@@ -781,7 +698,19 @@ namespace System.Diagnostics.Tracing
Debug.Assert(bufferLength >= (offset + 8));
*(long *)(buffer + offset) = value;
offset += 8;
- }
+ }
+
+ private static TypeCode GetTypeCodeExtended(Type parameterType)
+ {
+ // Guid is not part of TypeCode, we decided to use 17 to represent it, as it's the "free slot"
+ // see https://github.com/dotnet/coreclr/issues/16105#issuecomment-361749750 for more
+ const TypeCode GuidTypeCode = (TypeCode)17;
+
+ if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
+ return GuidTypeCode;
+
+ return Type.GetTypeCode(parameterType);
+ }
#endif
internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
@@ -825,6 +754,7 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
WriteEventCore(eventId, 1, descrs);
}
}
@@ -837,8 +767,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -851,10 +783,13 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)(&arg3);
descrs[2].Size = 4;
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
}
@@ -868,6 +803,7 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
+ descrs[0].Reserved = 0;
WriteEventCore(eventId, 1, descrs);
}
}
@@ -880,8 +816,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 8;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -894,10 +832,13 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 8;
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)(&arg3);
descrs[2].Size = 8;
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
}
@@ -914,6 +855,7 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[0].Reserved = 0;
WriteEventCore(eventId, 1, descrs);
}
}
@@ -932,8 +874,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)string2Bytes;
descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -954,10 +898,13 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)string2Bytes;
descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)string3Bytes;
descrs[2].Size = ((arg3.Length + 1) * 2);
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
}
@@ -975,8 +922,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -993,10 +942,13 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)(&arg3);
descrs[2].Size = 4;
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
}
@@ -1014,8 +966,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)string1Bytes;
descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&arg2);
descrs[1].Size = 8;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -1033,8 +987,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)string2Bytes;
descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -1052,8 +1008,10 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)string2Bytes;
descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -1070,8 +1028,10 @@ namespace System.Diagnostics.Tracing
int blobSize = 0;
descrs[0].DataPointer = (IntPtr)(&blobSize);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
descrs[1].Size = 0;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
else
@@ -1081,8 +1041,10 @@ namespace System.Diagnostics.Tracing
{
descrs[0].DataPointer = (IntPtr)(&blobSize);
descrs[0].Size = 4;
+ descrs[0].Reserved = 0;
descrs[1].DataPointer = (IntPtr)blob;
descrs[1].Size = blobSize;
+ descrs[1].Reserved = 0;
WriteEventCore(eventId, 2, descrs);
}
}
@@ -1097,13 +1059,16 @@ namespace System.Diagnostics.Tracing
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0].DataPointer = (IntPtr)(&arg1);
descrs[0].Size = 8;
+ descrs[0].Reserved = 0;
if (arg2 == null || arg2.Length == 0)
{
int blobSize = 0;
descrs[1].DataPointer = (IntPtr)(&blobSize);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
descrs[2].Size = 0;
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
else
@@ -1113,8 +1078,10 @@ namespace System.Diagnostics.Tracing
{
descrs[1].DataPointer = (IntPtr)(&blobSize);
descrs[1].Size = 4;
+ descrs[1].Reserved = 0;
descrs[2].DataPointer = (IntPtr)blob;
descrs[2].Size = blobSize;
+ descrs[2].Reserved = 0;
WriteEventCore(eventId, 3, descrs);
}
}
@@ -1139,6 +1106,12 @@ namespace System.Diagnostics.Tracing
/// </summary>
public int Size { get { return m_Size; } set { m_Size = value; } }
+ /// <summary>
+ /// Reserved by ETW. This property is present to ensure that we can zero it
+ /// since System.Private.CoreLib uses are not zero'd.
+ /// </summary>
+ internal int Reserved { get { return m_Reserved; } set { m_Reserved = value; } }
+
#region private
/// <summary>
/// Initializes the members of this EventData object to point at a previously-pinned
@@ -1180,8 +1153,10 @@ namespace System.Diagnostics.Tracing
/// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
/// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
/// descrs[0].Size = 8;
+ /// descrs[0].Reserved = 0;
/// descrs[1].DataPointer = (IntPtr)string2Bytes;
/// descrs[1].Size = ((arg2.Length + 1) * 2);
+ /// descrs[1].Reserved = 0;
/// WriteEventCore(eventId, 2, descrs);
/// }
/// }
@@ -1257,75 +1232,6 @@ namespace System.Diagnostics.Tracing
#if FEATURE_MANAGED_ETW
if (m_eventData[eventId].EnabledForETW)
{
-
-#if FEATURE_ACTIVITYSAMPLING
- // this code should be kept in sync with WriteEventVarargs().
- SessionMask etwSessions = SessionMask.All;
- // only compute etwSessions if there are *any* ETW filters enabled...
- if ((ulong)m_curLiveSessions != 0)
- etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
- // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
- // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
-
- if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
- {
- if (!SelfDescribingEvents)
- {
- if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
- {
- // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
- // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
- // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
- // mask set to 0x0f so, when all ETW sessions want the event we don't need to
- // synthesize a new one
- if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
- ThrowEventSourceException(m_eventData[eventId].Name);
- }
- else
- {
- long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
- // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
- // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
- // only some of the ETW sessions will receive this event. Synthesize a new
- // Descriptor whose Keywords field will have the appropriate bits set.
- // etwSessions might be 0, if there are legacy ETW listeners that want this event
- var desc = new EventDescriptor(
- m_eventData[eventId].Descriptor.EventId,
- m_eventData[eventId].Descriptor.Version,
- m_eventData[eventId].Descriptor.Channel,
- m_eventData[eventId].Descriptor.Level,
- m_eventData[eventId].Descriptor.Opcode,
- m_eventData[eventId].Descriptor.Task,
- unchecked((long)etwSessions.ToEventKeywords() | origKwd));
-
- if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
- ThrowEventSourceException(m_eventData[eventId].Name);
- }
- }
- else
- {
- TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
- if (tlet == null)
- {
- tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
- EventTags.None,
- m_eventData[eventId].Parameters);
- Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
-
- }
- long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
- // TODO: activity ID support
- EventSourceOptions opt = new EventSourceOptions
- {
- Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
- Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
- Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
- };
-
- WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
- }
- }
-#else
if (!SelfDescribingEvents)
{
if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
@@ -1351,7 +1257,6 @@ namespace System.Diagnostics.Tracing
WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
}
-#endif // FEATURE_ACTIVITYSAMPLING
}
#endif // FEATURE_MANAGED_ETW
@@ -1455,30 +1360,6 @@ namespace System.Diagnostics.Tracing
#endregion
#region private
-#if FEATURE_ACTIVITYSAMPLING
- internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
- {
- Debug.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
-
- if (m_eventSourceEnabled)
- {
- if (listener == null)
- {
- WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
- }
- else
- {
- EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
- eventCallbackArgs.EventId = 0;
- eventCallbackArgs.Message = msg;
- eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
- eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
- eventCallbackArgs.EventName = "EventSourceMessage";
- listener.OnEventWritten(eventCallbackArgs);
- }
- }
- }
-#endif
private unsafe void WriteEventRaw(
string eventName,
@@ -1543,10 +1424,6 @@ namespace System.Diagnostics.Tracing
m_name = eventSourceName;
m_guid = eventSourceGuid;
-#if FEATURE_ACTIVITYSAMPLING
- m_curLiveSessions = new SessionMask(0);
- m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
-#endif // FEATURE_ACTIVITYSAMPLING
//Enable Implicit Activity tracker
m_activityTracker = ActivityTracker.Instance;
@@ -2024,67 +1901,6 @@ namespace System.Diagnostics.Tracing
#if FEATURE_MANAGED_ETW
if (m_eventData[eventId].EnabledForETW)
{
-#if FEATURE_ACTIVITYSAMPLING
- // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
- SessionMask etwSessions = SessionMask.All;
- // only compute etwSessions if there are *any* ETW filters enabled...
- if ((ulong)m_curLiveSessions != 0)
- etwSessions = GetEtwSessionMask(eventId, childActivityID);
-
- if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
- {
- if (!SelfDescribingEvents)
- {
- if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
- {
- // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
- // mask set to 0x0f so, when all ETW sessions want the event we don't need to
- // synthesize a new one
- if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
- ThrowEventSourceException(m_eventData[eventId].Name);
- }
- else
- {
- long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
- // only some of the ETW sessions will receive this event. Synthesize a new
- // Descriptor whose Keywords field will have the appropriate bits set.
- var desc = new EventDescriptor(
- m_eventData[eventId].Descriptor.EventId,
- m_eventData[eventId].Descriptor.Version,
- m_eventData[eventId].Descriptor.Channel,
- m_eventData[eventId].Descriptor.Level,
- m_eventData[eventId].Descriptor.Opcode,
- m_eventData[eventId].Descriptor.Task,
- unchecked((long)etwSessions.ToEventKeywords() | origKwd));
-
- if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
- ThrowEventSourceException(m_eventData[eventId].Name);
- }
- }
- else
- {
- TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
- if (tlet == null)
- {
- tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
- EventTags.None,
- m_eventData[eventId].Parameters);
- Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
-
- }
- long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
- // TODO: activity ID support
- EventSourceOptions opt = new EventSourceOptions
- {
- Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
- Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
- Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
- };
-
- WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
- }
- }
-#else
if (!SelfDescribingEvents)
{
if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
@@ -2111,7 +1927,6 @@ namespace System.Diagnostics.Tracing
WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
}
-#endif // FEATURE_ACTIVITYSAMPLING
}
#endif // FEATURE_MANAGED_ETW
if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
@@ -2253,18 +2068,6 @@ namespace System.Diagnostics.Tracing
Debug.Assert(dispatcher.m_EventEnabled != null);
if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
{
-#if FEATURE_ACTIVITYSAMPLING
- var activityFilter = dispatcher.m_Listener.m_activityFilter;
- // order below is important as PassesActivityFilter will "flow" active activities
- // even when the current EventSource doesn't have filtering enabled. This allows
- // interesting activities to be updated so that sources that do sample can get
- // accurate data
- if (activityFilter == null ||
- ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
- m_eventData[eventId].TriggersActivityTracking > 0,
- this, eventId) ||
- !dispatcher.m_activityFilteringEnabled)
-#endif // FEATURE_ACTIVITYSAMPLING
{
try
{
@@ -2379,62 +2182,6 @@ namespace System.Diagnostics.Tracing
}
}
-#if FEATURE_ACTIVITYSAMPLING
- unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
- {
- SessionMask etwSessions = new SessionMask();
-
- for (int i = 0; i < SessionMask.MAX; ++i)
- {
- EtwSession etwSession = m_etwSessionIdMap[i];
- if (etwSession != null)
- {
- ActivityFilter activityFilter = etwSession.m_activityFilter;
- // PassesActivityFilter() will flow "interesting" activities, so make sure
- // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
- // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
- // do not fire events indiscriminately, when no filters are specified, but only
- // if, in addition, the session did not also enable ActivitySampling)
- if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
- activityFilter != null &&
- ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
- m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
- !m_activityFilteringForETWEnabled[i])
- {
- etwSessions[i] = true;
- }
- }
- }
- // flow "interesting" activities for all legacy sessions in which there's some
- // level of activity tracing enabled (even other EventSources)
- if (m_legacySessions != null && m_legacySessions.Count > 0 &&
- (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
- {
- // only calculate InternalCurrentThreadActivityId once
- Guid* pCurrentActivityId = null;
- Guid currentActivityId;
- foreach (var legacyEtwSession in m_legacySessions)
- {
- if (legacyEtwSession == null)
- continue;
-
- ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
- if (activityFilter != null)
- {
- if (pCurrentActivityId == null)
- {
- currentActivityId = InternalCurrentThreadActivityId;
- pCurrentActivityId = &currentActivityId;
- }
- ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
- }
- }
- }
-
- return etwSessions;
- }
-#endif // FEATURE_ACTIVITYSAMPLING
-
/// <summary>
/// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
/// It is possible that eventSources turn off the event based on additional filtering criteria.
@@ -2616,13 +2363,9 @@ namespace System.Diagnostics.Tracing
public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
-#if !FEATURE_ACTIVITYSAMPLING
#pragma warning disable 0649
-#endif
public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
-#if !FEATURE_ACTIVITYSAMPLING
#pragma warning restore 0649
-#endif
public string Name; // the name of the event
public string Message; // If the event has a message associated with it, this is it.
public ParameterInfo[] Parameters; // TODO can we remove?
@@ -2790,43 +2533,6 @@ namespace System.Diagnostics.Tracing
#endif
}
-#if FEATURE_ACTIVITYSAMPLING
- if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
- {
- bool participateInSampling = false;
- string activityFilters;
- int sessionIdBit;
-
- ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
- out activityFilters, out sessionIdBit);
-
- if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
- {
- throw new ArgumentException(SR.Format(SR.EventSource_SessionIdError,
- commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
- sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
- }
-
- if (commandArgs.listener == null)
- {
- UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
- }
- else
- {
- ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
- commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
- }
- }
- else if (!bSessionEnable && commandArgs.listener == null)
- {
- // if we disable an ETW session, indicate that in a synthesized command argument
- if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
- {
- commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
- }
- }
-#endif // FEATURE_ACTIVITYSAMPLING
-
// Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
// things like log messages, or test if keywords are enabled in the callback.
if (commandArgs.enable)
@@ -2840,47 +2546,11 @@ namespace System.Diagnostics.Tracing
if (eventCommandCallback != null)
eventCommandCallback(this, commandArgs);
-#if FEATURE_ACTIVITYSAMPLING
- if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
- {
- // if we disable an ETW session, complete disabling it
- UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
- }
-#endif // FEATURE_ACTIVITYSAMPLING
-
if (!commandArgs.enable)
{
// If we are disabling, maybe we can turn on 'quick checks' to filter
// quickly. These are all just optimizations (since later checks will still filter)
-#if FEATURE_ACTIVITYSAMPLING
- // Turn off (and forget) any information about Activity Tracing.
- if (commandArgs.listener == null)
- {
- // reset all filtering information for activity-tracing-aware sessions
- for (int i = 0; i < SessionMask.MAX; ++i)
- {
- EtwSession etwSession = m_etwSessionIdMap[i];
- if (etwSession != null)
- ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
- }
- m_activityFilteringForETWEnabled = new SessionMask(0);
- m_curLiveSessions = new SessionMask(0);
- // reset activity-tracing-aware sessions
- if (m_etwSessionIdMap != null)
- for (int i = 0; i < SessionMask.MAX; ++i)
- m_etwSessionIdMap[i] = null;
- // reset legacy sessions
- if (m_legacySessions != null)
- m_legacySessions.Clear();
- }
- else
- {
- ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
- commandArgs.dispatcher.m_activityFilteringEnabled = false;
- }
-#endif // FEATURE_ACTIVITYSAMPLING
-
// There is a good chance EnabledForAnyListener are not as accurate as
// they could be, go ahead and get a better estimate.
for (int i = 0; i < m_eventData.Length; i++)
@@ -2905,9 +2575,6 @@ namespace System.Diagnostics.Tracing
m_eventSourceEnabled = false;
}
}
-#if FEATURE_ACTIVITYSAMPLING
- UpdateKwdTriggers(commandArgs.enable);
-#endif // FEATURE_ACTIVITYSAMPLING
}
else
{
@@ -2930,14 +2597,6 @@ namespace System.Diagnostics.Tracing
if (eventCommandCallback != null)
eventCommandCallback(this, commandArgs);
}
-
-#if FEATURE_ACTIVITYSAMPLING
- if (m_completelyInited && (commandArgs.listener != null || shouldReport))
- {
- SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
- ReportActivitySamplingInfo(commandArgs.listener, m);
- }
-#endif // FEATURE_ACTIVITYSAMPLING
}
catch (Exception e)
{
@@ -2948,133 +2607,6 @@ namespace System.Diagnostics.Tracing
}
}
-#if FEATURE_ACTIVITYSAMPLING
-
- internal void UpdateEtwSession(
- int sessionIdBit,
- int etwSessionId,
- bool bEnable,
- string activityFilters,
- bool participateInSampling)
- {
- if (sessionIdBit < SessionMask.MAX)
- {
- // activity-tracing-aware etw session
- if (bEnable)
- {
- var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
- ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
- m_etwSessionIdMap[sessionIdBit] = etwSession;
- m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
- }
- else
- {
- var etwSession = EtwSession.GetEtwSession(etwSessionId);
- m_etwSessionIdMap[sessionIdBit] = null;
- m_activityFilteringForETWEnabled[sessionIdBit] = false;
- if (etwSession != null)
- {
- ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
- // the ETW session is going away; remove it from the global list
- EtwSession.RemoveEtwSession(etwSession);
- }
- }
- m_curLiveSessions[sessionIdBit] = bEnable;
- }
- else
- {
- // legacy etw session
- if (bEnable)
- {
- if (m_legacySessions == null)
- m_legacySessions = new List<EtwSession>(8);
- var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
- if (!m_legacySessions.Contains(etwSession))
- m_legacySessions.Add(etwSession);
- }
- else
- {
- var etwSession = EtwSession.GetEtwSession(etwSessionId);
- if (etwSession != null)
- {
- if (m_legacySessions != null)
- m_legacySessions.Remove(etwSession);
- // the ETW session is going away; remove it from the global list
- EtwSession.RemoveEtwSession(etwSession);
- }
- }
- }
- }
-
- internal static bool ParseCommandArgs(
- IDictionary<string, string> commandArguments,
- out bool participateInSampling,
- out string activityFilters,
- out int sessionIdBit)
- {
- bool res = true;
- participateInSampling = false;
- string activityFilterString;
- if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
- {
- // if a start event is specified default the event source to participate in sampling
- participateInSampling = true;
- }
-
- if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
- {
- if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
- activityFilterString == "0")
- participateInSampling = false;
- else
- participateInSampling = true;
- }
-
- string sSessionKwd;
- int sessionKwd = -1;
- if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
- !int.TryParse(sSessionKwd, out sessionKwd) ||
- sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
- sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
- {
- sessionIdBit = -1;
- res = false;
- }
- else
- {
- sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
- }
- return res;
- }
-
- internal void UpdateKwdTriggers(bool enable)
- {
- if (enable)
- {
- // recompute m_keywordTriggers
- ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
- if (gKeywords == 0)
- gKeywords = 0xFFFFffffFFFFffff;
-
- m_keywordTriggers = 0;
- for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
- {
- EtwSession etwSession = m_etwSessionIdMap[sessId];
- if (etwSession == null)
- continue;
-
- ActivityFilter activityFilter = etwSession.m_activityFilter;
- ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
- }
- }
- else
- {
- m_keywordTriggers = 0;
- }
- }
-
-#endif // FEATURE_ACTIVITYSAMPLING
-
/// <summary>
/// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
/// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
@@ -4148,48 +3680,6 @@ namespace System.Diagnostics.Tracing
}
}
-#if FEATURE_ACTIVITYSAMPLING
- private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
- {
- Debug.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
-
- for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
- {
- if (!sessions[perEventSourceSessionId])
- continue;
-
- ActivityFilter af;
- if (listener == null)
- {
- EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
- Debug.Assert(etwSession != null);
- af = etwSession.m_activityFilter;
- }
- else
- {
- af = listener.m_activityFilter;
- }
-
- if (af == null)
- continue;
-
- SessionMask m = new SessionMask();
- m[perEventSourceSessionId] = true;
-
- foreach (var t in af.GetFilterAsTuple(m_guid))
- {
- WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
- }
-
- bool participateInSampling = (listener == null) ?
- m_activityFilteringForETWEnabled[perEventSourceSessionId] :
- GetDispatcher(listener).m_activityFilteringEnabled;
- WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
- perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
- }
- }
-#endif // FEATURE_ACTIVITYSAMPLING
-
// private instance state
private string m_name; // My friendly name (privided in ctor)
internal int m_id; // A small integer that is unique to this instance.
@@ -4231,16 +3721,6 @@ namespace System.Diagnostics.Tracing
internal volatile ulong[] m_channelData;
#endif
-#if FEATURE_ACTIVITYSAMPLING
- private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
- private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
- private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
- internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
- internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
- static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
- // Also used to mark that activity tracing is on for some case
-#endif // FEATURE_ACTIVITYSAMPLING
-
// We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
// We have m_activityTracker field simply because instance field is more efficient than static field fetch.
ActivityTracker m_activityTracker;
@@ -4772,9 +4252,6 @@ namespace System.Diagnostics.Tracing
// Instance fields
internal volatile EventListener m_Next; // These form a linked list in s_Listeners
-#if FEATURE_ACTIVITYSAMPLING
- internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
-#endif // FEATURE_ACTIVITYSAMPLING
// static fields
@@ -5360,629 +4837,6 @@ namespace System.Diagnostics.Tracing
#region private classes
-#if FEATURE_ACTIVITYSAMPLING
-
- /// <summary>
- /// ActivityFilter is a helper structure that is used to keep track of run-time state
- /// associated with activity filtering. It is 1-1 with EventListeners (logically
- /// every listener has one of these, however we actually allocate them lazily), as well
- /// as 1-to-1 with tracing-aware EtwSessions.
- ///
- /// This structure also keeps track of the sampling counts associated with 'trigger'
- /// events. Because these trigger events are rare, and you typically only have one of
- /// them, we store them here as a linked list.
- /// </summary>
- internal sealed class ActivityFilter : IDisposable
- {
- /// <summary>
- /// Disable all activity filtering for the listener associated with 'filterList',
- /// (in the session associated with it) that is triggered by any event in 'source'.
- /// </summary>
- public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
- {
-#if !ES_BUILD_STANDALONE
- Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
-#endif
-
- if (filterList == null)
- return;
-
- ActivityFilter cur;
- // Remove it from anywhere in the list (except the first element, which has to
- // be treated specially)
- ActivityFilter prev = filterList;
- cur = prev.m_next;
- while (cur != null)
- {
- if (cur.m_providerGuid == source.Guid)
- {
- // update TriggersActivityTracking bit
- if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
- --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
-
- // Remove it from the linked list.
- prev.m_next = cur.m_next;
- // dispose of the removed node
- cur.Dispose();
- // update cursor
- cur = prev.m_next;
- }
- else
- {
- // update cursors
- prev = cur;
- cur = prev.m_next;
- }
- }
-
- // Sadly we have to treat the first element specially in linked list removal in C#
- if (filterList.m_providerGuid == source.Guid)
- {
- // update TriggersActivityTracking bit
- if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
- --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
-
- // We are the first element in the list.
- var first = filterList;
- filterList = first.m_next;
- // dispose of the removed node
- first.Dispose();
- }
- // the above might have removed the one ActivityFilter in the session that contains the
- // cleanup delegate; re-create the delegate if needed
- if (filterList != null)
- {
- EnsureActivityCleanupDelegate(filterList);
- }
- }
-
- /// <summary>
- /// Currently this has "override" semantics. We first disable all filters
- /// associated with 'source', and next we add new filters for each entry in the
- /// string 'startEvents'. participateInSampling specifies whether non-startEvents
- /// always trigger or only trigger when current activity is 'active'.
- /// </summary>
- public static void UpdateFilter(
- ref ActivityFilter filterList,
- EventSource source,
- int perEventSourceSessionId,
- string startEvents)
- {
-#if !ES_BUILD_STANDALONE
- Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
-#endif
-
- // first remove all filters associated with 'source'
- DisableFilter(ref filterList, source);
-
- if (!string.IsNullOrEmpty(startEvents))
- {
- // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
- // The Event may be specified by name or by ID. Errors in parsing such a pair
- // result in the error being reported to the listeners, and the pair being ignored.
- // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
- // we should initiate activity tracing once every 1000 events, *and* for event ID 12
- // we should initiate activity tracing once every 10 events.
- string[] activityFilterStrings = startEvents.Split(' ');
-
- for (int i = 0; i < activityFilterStrings.Length; ++i)
- {
- string activityFilterString = activityFilterStrings[i];
- int sampleFreq = 1;
- int eventId = -1;
- int colonIdx = activityFilterString.IndexOf(':');
- if (colonIdx < 0)
- {
- source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
- activityFilterString, false);
- // ignore failure...
- continue;
- }
- string sFreq = activityFilterString.Substring(colonIdx + 1);
- if (!int.TryParse(sFreq, out sampleFreq))
- {
- source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
- continue;
- }
- activityFilterString = activityFilterString.Substring(0, colonIdx);
- if (!int.TryParse(activityFilterString, out eventId))
- {
- // reset eventId
- eventId = -1;
- // see if it's an event name
- for (int j = 0; j < source.m_eventData.Length; j++)
- {
- EventSource.EventMetadata[] ed = source.m_eventData;
- if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
- string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
- {
- eventId = ed[j].Descriptor.EventId;
- break;
- }
- }
- }
- if (eventId < 0 || eventId >= source.m_eventData.Length)
- {
- source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
- continue;
- }
- EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
- }
- }
- }
-
- /// <summary>
- /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
- /// </summary>
- public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
- {
- for (var af = filterList; af != null; af = af.m_next)
- {
- if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
- return af;
- }
- return null;
- }
-
- /// <summary>
- /// Returns a session mask representing all sessions in which the activity
- /// associated with the current thread is allowed through the activity filter.
- /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
- /// most of the time this is false as you can guarantee this event is NOT a
- /// triggering event. If 'triggeringEvent' is true, then it checks the
- /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
- /// a trigger. If so it activates the current activity.
- ///
- /// If 'childActivityID' is present, it will be added to the active set if the
- /// current activity is active.
- /// </summary>
- unsafe public static bool PassesActivityFilter(
- ActivityFilter filterList,
- Guid* childActivityID,
- bool triggeringEvent,
- EventSource source,
- int eventId)
- {
- Debug.Assert(filterList != null && filterList.m_activeActivities != null);
- bool shouldBeLogged = false;
- if (triggeringEvent)
- {
- for (ActivityFilter af = filterList; af != null; af = af.m_next)
- {
- if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
- {
- // Update the sampling count with wrap-around
- int curSampleCount, newSampleCount;
- do
- {
- curSampleCount = af.m_curSampleCount;
- if (curSampleCount <= 1)
- newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
- else
- newSampleCount = curSampleCount - 1;
- }
- while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
- // If we hit zero, then start tracking the activity.
- if (curSampleCount <= 1)
- {
- Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
- Tuple<Guid, int> startId;
- // only add current activity if it's not already a root activity
- if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
- {
- // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
- shouldBeLogged = true;
- af.m_activeActivities[currentActivityId] = Environment.TickCount;
- af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
- }
- }
- else
- {
- // a start event following a triggering start event
- Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
- Tuple<Guid, int> startId;
- // only remove current activity if we added it
- if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
- startId.Item1 == source.Guid && startId.Item2 == eventId)
- {
- // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
- // remove activity only from current logging scope (af)
- int dummy;
- af.m_activeActivities.TryRemove(currentActivityId, out dummy);
- }
- }
- break;
- }
- }
- }
-
- var activeActivities = GetActiveActivities(filterList);
- if (activeActivities != null)
- {
- // if we hadn't already determined this should be logged, test further
- if (!shouldBeLogged)
- {
- shouldBeLogged = !activeActivities.IsEmpty &&
- activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
- }
- if (shouldBeLogged && childActivityID != null &&
- ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
- {
- FlowActivityIfNeeded(filterList, null, childActivityID);
- // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
- }
- }
- // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
- return shouldBeLogged;
- }
-
- public static bool IsCurrentActivityActive(ActivityFilter filterList)
- {
- var activeActivities = GetActiveActivities(filterList);
- if (activeActivities != null &&
- activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
- return true;
-
- return false;
- }
-
- /// <summary>
- /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
- /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
- /// value for 'currentActivityid' is an indication the caller has already verified
- /// that the current activity is active.
- /// </summary>
- unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
- {
- Debug.Assert(childActivityID != null);
-
- var activeActivities = GetActiveActivities(filterList);
- Debug.Assert(activeActivities != null);
-
- // take currentActivityId == null to mean we *know* the current activity is "active"
- if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
- return;
-
- if (activeActivities.Count > MaxActivityTrackCount)
- {
- TrimActiveActivityStore(activeActivities);
- // make sure current activity is still in the set:
- activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
- }
- // add child activity to list of activities
- activeActivities[*childActivityID] = Environment.TickCount;
-
- }
-
- /// <summary>
- /// </summary>
- public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
- {
- for (var af = activityFilter; af != null; af = af.m_next)
- {
- if ((sourceGuid == af.m_providerGuid) &&
- (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
- ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
- {
- // we could be more precise here, if we tracked 'anykeywords' per session
- unchecked
- {
- source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
- }
- }
- }
- }
-
- /// <summary>
- /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
- /// associated with 'this' ActivityFilter list, return configured sequence of
- /// [eventId, sampleFreq] pairs that defines the sampling policy.
- /// </summary>
- public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
- {
- for (ActivityFilter af = this; af != null; af = af.m_next)
- {
- if (af.m_providerGuid == sourceGuid)
- yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
- }
- }
-
- /// <summary>
- /// The cleanup being performed consists of removing the m_myActivityDelegate from
- /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
- /// </summary>
- public void Dispose()
- {
-#if !ES_BUILD_STANDALONE
- Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
-#endif
- // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
- // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
- // during the finalization of the ActivityFilter
- if (m_myActivityDelegate != null)
- {
- EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
- m_myActivityDelegate = null;
- }
- }
-
-#region private
-
- /// <summary>
- /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
- /// 'samplingFreq' times the event fires. You can have several of these forming a
- /// linked list.
- /// </summary>
- private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
- {
- m_providerGuid = source.Guid;
- m_perEventSourceSessionId = perEventSourceSessionId;
- m_eventId = eventId;
- m_samplingFreq = samplingFreq;
- m_next = existingFilter;
-
- Debug.Assert(existingFilter == null ||
- (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
-
- // if this is the first filter we add for this session, we need to create a new
- // table of activities. m_activeActivities is common across EventSources in the same
- // session
- ConcurrentDictionary<Guid, int> activeActivities = null;
- if (existingFilter == null ||
- (activeActivities = GetActiveActivities(existingFilter)) == null)
- {
- m_activeActivities = new ConcurrentDictionary<Guid, int>();
- m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
-
- // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
- m_myActivityDelegate = GetActivityDyingDelegate(this);
- EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
- }
- else
- {
- m_activeActivities = activeActivities;
- m_rootActiveActivities = existingFilter.m_rootActiveActivities;
- }
-
- }
-
- /// <summary>
- /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
- /// activity-removing delegate for the listener/session associated with 'filterList'.
- /// </summary>
- private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
- {
- if (filterList == null)
- return;
-
- for (ActivityFilter af = filterList; af != null; af = af.m_next)
- {
- if (af.m_myActivityDelegate != null)
- return;
- }
-
- // we didn't find a delegate
- filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
- EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
- }
-
- /// <summary>
- /// Builds the delegate to be called when an activity is dying. This is responsible
- /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
- /// This gets "added" to EventSource.s_activityDying and ends up being called from
- /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
- /// </summary>
- /// <returns>The delegate to be called when an activity is dying</returns>
- private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
- {
- return (Guid oldActivity) =>
- {
- int dummy;
- filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
- Tuple<Guid, int> dummyTuple;
- filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
- };
- }
-
- /// <summary>
- /// Enables activity filtering for the listener associated with 'filterList', triggering on
- /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
- ///
- /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
- /// activitySampling if something else triggered).
- /// </summary>
- /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
- private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
- {
-#if !ES_BUILD_STANDALONE
- Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
-#endif
- Debug.Assert(samplingFreq > 0);
- Debug.Assert(eventId >= 0);
-
- filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
-
- // Mark the 'quick Check' that indicates this is a trigger event.
- // If eventId is out of range then this mark is not done which has the effect of ignoring
- // the trigger.
- if (0 <= eventId && eventId < source.m_eventData.Length)
- ++source.m_eventData[eventId].TriggersActivityTracking;
-
- return true;
- }
-
- /// <summary>
- /// Normally this code never runs, it is here just to prevent run-away resource usage.
- /// </summary>
- private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
- {
- if (activities.Count > MaxActivityTrackCount)
- {
- // Remove half of the oldest activity ids.
- var keyValues = activities.ToArray();
- var tickNow = Environment.TickCount;
-
- // Sort by age, taking into account wrap-around. As long as x and y are within
- // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
- // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
- Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
- for (int i = 0; i < keyValues.Length / 2; i++)
- {
- int dummy;
- activities.TryRemove(keyValues[i].Key, out dummy);
- }
- }
- }
-
- private static ConcurrentDictionary<Guid, int> GetActiveActivities(
- ActivityFilter filterList)
- {
- for (ActivityFilter af = filterList; af != null; af = af.m_next)
- {
- if (af.m_activeActivities != null)
- return af.m_activeActivities;
- }
- return null;
- }
-
- // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
- // in the m_next list. The 'int' value in the m_activities set is a timestamp
- // (Environment.TickCount) of when the entry was put in the system and is used to
- // remove 'old' entries that if the set gets too big.
- ConcurrentDictionary<Guid, int> m_activeActivities;
-
- // m_rootActiveActivities holds the "root" active activities, i.e. the activities
- // that were marked as active because a Start event fired on them. We need to keep
- // track of these to enable sampling in the scenario of an app's main thread that
- // never explicitly sets distinct activity IDs as it executes. To handle these
- // situations we manufacture a Guid from the thread's ID, and:
- // (a) we consider the firing of a start event when the sampling counter reaches
- // zero to mark the beginning of an interesting activity, and
- // (b) we consider the very next firing of the same start event to mark the
- // ending of that activity.
- // We use a ConcurrentDictionary to avoid taking explicit locks.
- // The key (a guid) represents the activity ID of the root active activity
- // The value is made up of the Guid of the event provider and the eventId of
- // the start event.
- ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
- Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
- int m_eventId; // triggering event
- int m_samplingFreq; // Counter reset to this when it hits 0
- int m_curSampleCount; // We count down to 0 and then activate the activity.
- int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
-
- const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
-
- ActivityFilter m_next; // We create a linked list of these
- Action<Guid> m_myActivityDelegate;
-#endregion
- };
-
-
- /// <summary>
- /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
- /// are limited to 8 concurrent sessions per machine (currently) we're going to store
- /// the active ones in a singly linked list.
- /// </summary>
- internal class EtwSession
- {
- public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
- {
- if (etwSessionId < 0)
- return null;
-
- EtwSession etwSession;
- foreach (var wrEtwSession in s_etwSessions)
- {
-#if ES_BUILD_STANDALONE
- if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
- return etwSession;
-#else
- if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
- return etwSession;
-#endif
- }
-
- if (!bCreateIfNeeded)
- return null;
-
-#if ES_BUILD_STANDALONE
- if (s_etwSessions == null)
- s_etwSessions = new List<WeakReference>();
-
- etwSession = new EtwSession(etwSessionId);
- s_etwSessions.Add(new WeakReference(etwSession));
-#else
- if (s_etwSessions == null)
- s_etwSessions = new List<WeakReference<EtwSession>>();
-
- etwSession = new EtwSession(etwSessionId);
- s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
-#endif
-
- if (s_etwSessions.Count > s_thrSessionCount)
- TrimGlobalList();
-
- return etwSession;
-
- }
-
- public static void RemoveEtwSession(EtwSession etwSession)
- {
- Debug.Assert(etwSession != null);
- if (s_etwSessions == null || etwSession == null)
- return;
-
- s_etwSessions.RemoveAll((wrEtwSession) =>
- {
- EtwSession session;
-#if ES_BUILD_STANDALONE
- return (session = (EtwSession) wrEtwSession.Target) != null &&
- (session.m_etwSessionId == etwSession.m_etwSessionId);
-#else
- return wrEtwSession.TryGetTarget(out session) &&
- (session.m_etwSessionId == etwSession.m_etwSessionId);
-#endif
- });
-
- if (s_etwSessions.Count > s_thrSessionCount)
- TrimGlobalList();
- }
-
- private static void TrimGlobalList()
- {
- if (s_etwSessions == null)
- return;
-
- s_etwSessions.RemoveAll((wrEtwSession) =>
- {
-#if ES_BUILD_STANDALONE
- return wrEtwSession.Target == null;
-#else
- EtwSession session;
- return !wrEtwSession.TryGetTarget(out session);
-#endif
- });
- }
-
- private EtwSession(int etwSessionId)
- {
- m_etwSessionId = etwSessionId;
- }
-
- public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
- public ActivityFilter m_activityFilter; // all filters enabled for this session
-
-#if ES_BUILD_STANDALONE
- private static List<WeakReference> s_etwSessions = new List<WeakReference>();
-#else
- private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
-#endif
- private const int s_thrSessionCount = 16;
- }
-
-#endif // FEATURE_ACTIVITYSAMPLING
-
// holds a bitfield representing a session mask
/// <summary>
/// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
@@ -6096,9 +4950,6 @@ namespace System.Diagnostics.Tracing
// Instance fields
readonly internal EventListener m_Listener; // The dispatcher this entry is for
internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
-#if FEATURE_ACTIVITYSAMPLING
- internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
-#endif // FEATURE_ACTIVITYSAMPLING
// Only guaranteed to exist after a InsureInit()
internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
index 901a0ed1a..9b58d8251 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
@@ -187,8 +187,14 @@ namespace System.Diagnostics.Tracing
public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
{
- var ticks = value.ScalarValue.AsDateTime.Ticks;
- collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000);
+ DateTime dateTime = value.ScalarValue.AsDateTime;
+ const long UTCMinTicks = 504911232000000000;
+ long dateTimeTicks = 0;
+ // We cannot translate dates sooner than 1/1/1601 in UTC.
+ // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks
+ if (dateTime.Ticks > UTCMinTicks)
+ dateTimeTicks = dateTime.ToFileTimeUtc();
+ collector.AddScalar(dateTimeTicks);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
index bf29d7184..4348df7d6 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -7,10 +7,6 @@
#if PLATFORM_WINDOWS
#define FEATURE_MANAGED_ETW
-
-#if !ES_BUILD_STANDALONE
-#define FEATURE_ACTIVITYSAMPLING
-#endif
#endif // PLATFORM_WINDOWS
#if ES_BUILD_STANDALONE
@@ -438,6 +434,8 @@ namespace System.Diagnostics.Tracing
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+ for(int i = 0; i < eventTypes.dataCount + 3; i++)
+ descriptors[i] = default(EventData);
var pins = stackalloc GCHandle[pinCount];
for (int i = 0; i < pinCount; i++)
@@ -542,7 +540,10 @@ namespace System.Diagnostics.Tracing
// We make a descriptor for each EventData, and because we morph strings to counted strings
// we may have 2 for each arg, so we allocate enough for this.
- var descriptors = stackalloc EventData[eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3];
+ var descriptorsLength = eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3;
+ var descriptors = stackalloc EventData[descriptorsLength];
+ for(int i = 0; i < descriptorsLength; i++)
+ descriptors[i] = default(EventData);
fixed (byte*
pMetadata0 = this.providerMetadata,
@@ -618,6 +619,8 @@ namespace System.Diagnostics.Tracing
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+ for(int i=0; i<eventTypes.dataCount + 3; i++)
+ descriptors[i] = default(EventData);
var pins = stackalloc GCHandle[pinCount];
for (int i = 0; i < pinCount; i++)
diff --git a/src/System.Private.CoreLib/shared/System/Double.cs b/src/System.Private.CoreLib/shared/System/Double.cs
index 3652963ef..146ee4600 100644
--- a/src/System.Private.CoreLib/shared/System/Double.cs
+++ b/src/System.Private.CoreLib/shared/System/Double.cs
@@ -17,6 +17,8 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
+using Internal.Runtime.CompilerServices;
+
namespace System
{
[Serializable]
@@ -221,16 +223,19 @@ namespace System
//The hashcode for a double is the absolute value of the integer representation
//of that double.
//
- public unsafe override int GetHashCode()
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // 64-bit constants make the IL unusually large that makes the inliner to reject the method
+ public override int GetHashCode()
{
- double d = m_value;
- if (d == 0)
+ var bits = Unsafe.As<double, long>(ref m_value);
+
+ // Optimized check for IsNan() || IsZero()
+ if (((bits - 1) & 0x7FFFFFFFFFFFFFFF) >= 0x7FF0000000000000)
{
- // Ensure that 0 and -0 have the same hash code
- return 0;
+ // Ensure that all NaNs and both zeros have the same hash code
+ bits &= 0x7FF0000000000000;
}
- long value = *(long*)(&d);
- return unchecked((int)value) ^ ((int)(value >> 32));
+
+ return unchecked((int)bits) ^ ((int)(bits >> 32));
}
public override String ToString()
@@ -340,7 +345,7 @@ namespace System
bool success = Number.TryParseDouble(s, style, info, out result);
if (!success)
{
- ReadOnlySpan<char> sTrim = StringSpanHelpers.Trim(s);
+ ReadOnlySpan<char> sTrim = s.Trim();
if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol))
{
result = PositiveInfinity;
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs
index 3a8029d9a..17d6ed7a0 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs
@@ -4,9 +4,9 @@
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.InteropServices;
using System.Security;
using System.Text;
+using Internal.Runtime.CompilerServices;
namespace System.Globalization
{
@@ -45,10 +45,27 @@ namespace System.Globalization
result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.DayNames, out this.saDayNames);
result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.AbbrevDayNames, out this.saAbbrevDayNames);
result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.SuperShortDayNames, out this.saSuperShortDayNames);
- result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthNames, out this.saMonthNames);
- result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthNames, out this.saAbbrevMonthNames);
- result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthGenitiveNames, out this.saMonthGenitiveNames);
- result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthGenitiveNames, out this.saAbbrevMonthGenitiveNames);
+
+ string leapHebrewMonthName = null;
+ result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthNames, out this.saMonthNames, ref leapHebrewMonthName);
+ if (leapHebrewMonthName != null)
+ {
+ // In Hebrew calendar, get the leap month name Adar II and override the non-leap month 7
+ Debug.Assert(calendarId == CalendarId.HEBREW && saMonthNames.Length == 13);
+ saLeapYearMonthNames = (string[]) saMonthNames.Clone();
+ saLeapYearMonthNames[6] = leapHebrewMonthName;
+
+ // The returned data from ICU has 6th month name as 'Adar I' and 7th month name as 'Adar'
+ // We need to adjust that in the list used with non-leap year to have 6th month as 'Adar' and 7th month as 'Adar II'
+ // note that when formatting non-leap year dates, 7th month shouldn't get used at all.
+ saMonthNames[5] = saMonthNames[6];
+ saMonthNames[6] = leapHebrewMonthName;
+
+ }
+ result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthNames, out this.saAbbrevMonthNames, ref leapHebrewMonthName);
+ result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthGenitiveNames, out this.saMonthGenitiveNames, ref leapHebrewMonthName);
+ result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthGenitiveNames, out this.saAbbrevMonthGenitiveNames, ref leapHebrewMonthName);
+
result &= EnumEraNames(localeName, calendarId, CalendarDataType.EraNames, out this.saEraNames);
result &= EnumEraNames(localeName, calendarId, CalendarDataType.AbbrevEraNames, out this.saAbbrevEraNames);
@@ -68,7 +85,7 @@ namespace System.Globalization
Debug.Assert(!GlobalizationMode.Invariant);
// NOTE: there are no 'user overrides' on Linux
- int count = Interop.GlobalizationInterop.GetCalendars(localeName, calendars, calendars.Length);
+ int count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length);
// ensure there is at least 1 calendar returned
if (count == 0 && calendars.Length > 0)
@@ -93,7 +110,7 @@ namespace System.Globalization
return Interop.CallStringMethod(
(locale, calId, type, stringBuilder) =>
- Interop.GlobalizationInterop.GetCalendarInfo(
+ Interop.Globalization.GetCalendarInfo(
locale,
calId,
type,
@@ -109,9 +126,10 @@ namespace System.Globalization
{
datePatterns = null;
- CallbackContext callbackContext = new CallbackContext();
+ EnumCalendarsData callbackContext = new EnumCalendarsData();
+ callbackContext.Results = new List<string>();
callbackContext.DisallowDuplicates = true;
- bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext);
+ bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
if (result)
{
List<string> datePatternsList = callbackContext.Results;
@@ -240,12 +258,13 @@ namespace System.Globalization
return index - startIndex;
}
- private static bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] monthNames)
+ private static bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] monthNames, ref string leapHebrewMonthName)
{
monthNames = null;
- CallbackContext callbackContext = new CallbackContext();
- bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext);
+ EnumCalendarsData callbackContext = new EnumCalendarsData();
+ callbackContext.Results = new List<string>();
+ bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
if (result)
{
// the month-name arrays are expected to have 13 elements. If ICU only returns 12, add an
@@ -255,6 +274,17 @@ namespace System.Globalization
callbackContext.Results.Add(string.Empty);
}
+ if (callbackContext.Results.Count > 13)
+ {
+ Debug.Assert(calendarId == CalendarId.HEBREW && callbackContext.Results.Count == 14);
+
+ if (calendarId == CalendarId.HEBREW)
+ {
+ leapHebrewMonthName = callbackContext.Results[13];
+ }
+ callbackContext.Results.RemoveRange(13, callbackContext.Results.Count - 13);
+ }
+
monthNames = callbackContext.Results.ToArray();
}
@@ -280,8 +310,9 @@ namespace System.Globalization
{
calendarData = null;
- CallbackContext callbackContext = new CallbackContext();
- bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext);
+ EnumCalendarsData callbackContext = new EnumCalendarsData();
+ callbackContext.Results = new List<string>();
+ bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
if (result)
{
calendarData = callbackContext.Results.ToArray();
@@ -290,24 +321,16 @@ namespace System.Globalization
return result;
}
- private static bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, CallbackContext callbackContext)
+ private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref EnumCalendarsData callbackContext)
{
- GCHandle context = GCHandle.Alloc(callbackContext);
- try
- {
- return Interop.GlobalizationInterop.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)context);
- }
- finally
- {
- context.Free();
- }
+ return Interop.Globalization.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)Unsafe.AsPointer(ref callbackContext));
}
- private static void EnumCalendarInfoCallback(string calendarString, IntPtr context)
+ private static unsafe void EnumCalendarInfoCallback(string calendarString, IntPtr context)
{
try
{
- CallbackContext callbackContext = (CallbackContext)((GCHandle)context).Target;
+ ref EnumCalendarsData callbackContext = ref Unsafe.As<byte, EnumCalendarsData>(ref *(byte*)context);
if (callbackContext.DisallowDuplicates)
{
@@ -331,17 +354,10 @@ namespace System.Globalization
}
}
- private class CallbackContext
+ private struct EnumCalendarsData
{
- private List<string> _results = new List<string>();
-
- public CallbackContext()
- {
- }
-
- public List<string> Results { get { return _results; } }
-
- public bool DisallowDuplicates { get; set; }
+ public List<string> Results;
+ public bool DisallowDuplicates;
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs b/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs
index a67761ac6..03f9088d6 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs
@@ -274,7 +274,7 @@ namespace System.Globalization
}
// Context for EnumCalendarInfoExEx callback.
- private class EnumData
+ private struct EnumData
{
public string userOverride;
public List<string> strings;
@@ -427,7 +427,7 @@ namespace System.Globalization
//
// struct to help our calendar data enumaration callback
//
- private class EnumCalendarsData
+ private struct EnumCalendarsData
{
public int userOverride; // user override value (if found)
public List<int> calendars; // list of calendars found so far
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs
index 388127228..0cd8429bb 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs
@@ -276,7 +276,7 @@ namespace System.Globalization
public static UnicodeCategory GetUnicodeCategory(char ch)
{
- return (InternalGetUnicodeCategory(ch));
+ return (GetUnicodeCategory((int)ch));
}
public static UnicodeCategory GetUnicodeCategory(String s, int index)
@@ -290,9 +290,9 @@ namespace System.Globalization
return InternalGetUnicodeCategory(s, index);
}
- internal static unsafe UnicodeCategory InternalGetUnicodeCategory(int ch)
+ public static UnicodeCategory GetUnicodeCategory(int codePoint)
{
- return ((UnicodeCategory)InternalGetCategoryValue(ch, UNICODE_CATEGORY_OFFSET));
+ return ((UnicodeCategory)InternalGetCategoryValue(codePoint, UNICODE_CATEGORY_OFFSET));
}
@@ -352,7 +352,7 @@ namespace System.Globalization
Debug.Assert(value != null, "value can not be null");
Debug.Assert(index < value.Length, "index < value.Length");
- return (InternalGetUnicodeCategory(InternalConvertToUtf32(value, index)));
+ return (GetUnicodeCategory(InternalConvertToUtf32(value, index)));
}
internal static BidiCategory GetBidiCategory(String s, int index)
@@ -381,7 +381,7 @@ namespace System.Globalization
Debug.Assert(str.Length > 0, "str.Length > 0"); ;
Debug.Assert(index >= 0 && index < str.Length, "index >= 0 && index < str.Length");
- return (InternalGetUnicodeCategory(InternalConvertToUtf32(str, index, out charLength)));
+ return (GetUnicodeCategory(InternalConvertToUtf32(str, index, out charLength)));
}
internal static bool IsCombiningCategory(UnicodeCategory uc)
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs
index 13725bcc5..29e4f5321 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
namespace System.Globalization
{
@@ -26,6 +27,18 @@ namespace System.Globalization
}
}
+ internal static unsafe int InvariantIndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(source.Length != 0);
+ Debug.Assert(value.Length != 0);
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pValue = &MemoryMarshal.GetReference(value))
+ {
+ return InvariantFindString(pSource, source.Length, pValue, value.Length, ignoreCase, start: true);
+ }
+ }
+
internal static unsafe int InvariantLastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase)
{
Debug.Assert(source != null);
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
index e088a82de..c369c816b 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
@@ -57,6 +57,9 @@ namespace System.Globalization
~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);
+ // Cache the invariant CompareInfo
+ internal static readonly CompareInfo Invariant = CultureInfo.InvariantCulture.CompareInfo;
+
//
// CompareInfos have an interesting identity. They are attached to the locale that created them,
// ie: en-US would have an en-US sort. For haw-US (custom), then we serialize it as haw-US.
@@ -91,7 +94,7 @@ namespace System.Globalization
** culture the ID of the culture
** assembly the assembly which contains the sorting table.
**Exceptions:
- ** ArugmentNullException when the assembly is null
+ ** ArgumentNullException when the assembly is null
** ArgumentException if culture is invalid.
============================================================================*/
// Assembly constructor should be deprecated, we don't act on the assembly information any more
@@ -118,7 +121,7 @@ namespace System.Globalization
** name the name of the culture
** assembly the assembly which contains the sorting table.
**Exceptions:
- ** ArugmentNullException when the assembly is null
+ ** ArgumentNullException when the assembly is null
** ArgumentException if name is invalid.
============================================================================*/
// Assembly constructor should be deprecated, we don't act on the assembly information any more
@@ -295,7 +298,7 @@ namespace System.Globalization
return (Compare(string1, string2, CompareOptions.None));
}
- public unsafe virtual int Compare(string string1, string string2, CompareOptions options)
+ public virtual int Compare(string string1, string string2, CompareOptions options)
{
if (options == CompareOptions.OrdinalIgnoreCase)
{
@@ -341,17 +344,17 @@ namespace System.Globalization
return String.CompareOrdinal(string1, string2);
}
- return CompareString(string1.AsReadOnlySpan(), string2.AsReadOnlySpan(), options);
+ return CompareString(string1.AsSpan(), string2.AsSpan(), options);
}
// TODO https://github.com/dotnet/coreclr/issues/13827:
// This method shouldn't be necessary, as we should be able to just use the overload
// that takes two spans. But due to this issue, that's adding significant overhead.
- internal unsafe int Compare(ReadOnlySpan<char> string1, string string2, CompareOptions options)
+ internal int Compare(ReadOnlySpan<char> string1, string string2, CompareOptions options)
{
if (options == CompareOptions.OrdinalIgnoreCase)
{
- return CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan());
+ return CompareOrdinalIgnoreCase(string1, string2.AsSpan());
}
// Verify the options before we do any real comparison.
@@ -362,7 +365,7 @@ namespace System.Globalization
throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options));
}
- return string.CompareOrdinal(string1, string2.AsReadOnlySpan());
+ return string.CompareOrdinal(string1, string2.AsSpan());
}
if ((options & ValidCompareMaskOffFlags) != 0)
@@ -379,15 +382,15 @@ namespace System.Globalization
if (_invariantMode)
{
return (options & CompareOptions.IgnoreCase) != 0 ?
- CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan()) :
- string.CompareOrdinal(string1, string2.AsReadOnlySpan());
+ CompareOrdinalIgnoreCase(string1, string2.AsSpan()) :
+ string.CompareOrdinal(string1, string2.AsSpan());
}
return CompareString(string1, string2, options);
}
// TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly?
- internal unsafe virtual int Compare(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
+ internal virtual int Compare(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
{
if (options == CompareOptions.OrdinalIgnoreCase)
{
@@ -433,7 +436,7 @@ namespace System.Globalization
////////////////////////////////////////////////////////////////////////
- public unsafe virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2)
+ public virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2)
{
return Compare(string1, offset1, length1, string2, offset2, length2, 0);
}
@@ -523,8 +526,8 @@ namespace System.Globalization
}
return CompareString(
- string1.AsReadOnlySpan().Slice(offset1, length1),
- string2.AsReadOnlySpan().Slice(offset2, length2),
+ string1.AsSpan().Slice(offset1, length1),
+ string2.AsSpan().Slice(offset2, length2),
options);
}
@@ -544,11 +547,11 @@ namespace System.Globalization
// it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by
// calling the OS.
//
- internal static unsafe int CompareOrdinalIgnoreCase(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB)
+ internal static int CompareOrdinalIgnoreCase(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB)
{
Debug.Assert(indexA + lengthA <= strA.Length);
Debug.Assert(indexB + lengthB <= strB.Length);
- return CompareOrdinalIgnoreCase(strA.AsReadOnlySpan().Slice(indexA, lengthA), strB.AsReadOnlySpan().Slice(indexB, lengthB));
+ return CompareOrdinalIgnoreCase(strA.AsSpan().Slice(indexA, lengthA), strB.AsSpan().Slice(indexB, lengthB));
}
internal static unsafe int CompareOrdinalIgnoreCase(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
@@ -563,7 +566,7 @@ namespace System.Globalization
char* b = bp;
// in InvariantMode we support all range and not only the ascii characters.
- char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80);
+ char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x7F);
while (length != 0 && (*a <= maxChar) && (*b <= maxChar))
{
@@ -650,6 +653,17 @@ namespace System.Globalization
return StartsWith(source, prefix, options);
}
+ internal bool IsPrefix(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+ {
+ Debug.Assert(prefix.Length != 0);
+ Debug.Assert(source.Length != 0);
+ Debug.Assert((options & ValidIndexMaskOffFlags) == 0);
+ Debug.Assert(!_invariantMode);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return StartsWith(source, prefix, options);
+ }
+
public virtual bool IsPrefix(string source, string prefix)
{
return (IsPrefix(source, prefix, 0));
@@ -704,6 +718,17 @@ namespace System.Globalization
return EndsWith(source, suffix, options);
}
+ internal bool IsSuffix(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+ {
+ Debug.Assert(suffix.Length != 0);
+ Debug.Assert(source.Length != 0);
+ Debug.Assert((options & ValidIndexMaskOffFlags) == 0);
+ Debug.Assert(!_invariantMode);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return EndsWith(source, suffix, options);
+ }
+
public virtual bool IsSuffix(string source, string suffix)
{
@@ -816,6 +841,11 @@ namespace System.Globalization
if (count < 0 || startIndex > source.Length - count)
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
+ if (source.Length == 0)
+ {
+ return -1;
+ }
+
if (options == CompareOptions.OrdinalIgnoreCase)
{
return source.IndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
@@ -825,7 +855,7 @@ namespace System.Globalization
// Ordinal can't be selected with other flags
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
-
+
if (_invariantMode)
return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
@@ -880,6 +910,18 @@ namespace System.Globalization
return IndexOfCore(source, value, startIndex, count, options, null);
}
+ internal virtual int IndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(!_invariantMode);
+ return IndexOfOrdinalCore(source, value, ignoreCase);
+ }
+
+ internal unsafe virtual int IndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+ return IndexOfCore(source, value, options, null);
+ }
+
// The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated
// and the caller is passing a valid matchLengthPtr pointer.
internal unsafe int IndexOf(string source, string value, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
@@ -1256,7 +1298,7 @@ namespace System.Globalization
}
//
- // GetHashCodeOfString does more parameters validation. basically will throw when
+ // GetHashCodeOfString does more parameters validation. basically will throw when
// having Ordinal, OrdinalIgnoreCase and StringSort
//
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs
index 3b4b60fc8..3fce52792 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs
@@ -33,7 +33,7 @@ namespace System.Globalization
string realNameBuffer = _sRealName;
// Basic validation
- if (realNameBuffer.Contains("@"))
+ if (realNameBuffer.Contains('@'))
{
return false; // don't allow ICU variants to come in directly
}
@@ -43,7 +43,7 @@ namespace System.Globalization
if (index > 0)
{
if (index >= (realNameBuffer.Length - 1) // must have characters after _
- || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed
+ || realNameBuffer.Substring(index + 1).Contains('_')) // only one _ allowed
{
return false; // fail
}
@@ -91,7 +91,7 @@ namespace System.Globalization
{
// Get the locale name from ICU
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
- if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity))
+ if (!Interop.Globalization.GetLocaleName(localeName, sb, sb.Capacity))
{
StringBuilderCache.Release(sb);
windowsName = null;
@@ -107,7 +107,7 @@ namespace System.Globalization
{
// Get the default (system) locale name from ICU
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
- if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity))
+ if (!Interop.Globalization.GetDefaultLocaleName(sb, sb.Capacity))
{
StringBuilderCache.Release(sb);
windowsName = null;
@@ -143,7 +143,7 @@ namespace System.Globalization
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY);
- bool result = Interop.GlobalizationInterop.GetLocaleInfoString(localeName, (uint)type, sb, sb.Capacity);
+ bool result = Interop.Globalization.GetLocaleInfoString(localeName, (uint)type, sb, sb.Capacity);
if (!result)
{
// Failed, just use empty string
@@ -169,7 +169,7 @@ namespace System.Globalization
int value = 0;
- bool result = Interop.GlobalizationInterop.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value);
+ bool result = Interop.Globalization.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value);
if (!result)
{
// Failed, just use 0
@@ -185,7 +185,7 @@ namespace System.Globalization
int primaryGroupingSize = 0;
int secondaryGroupingSize = 0;
- bool result = Interop.GlobalizationInterop.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize);
+ bool result = Interop.Globalization.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize);
if (!result)
{
Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed");
@@ -210,7 +210,7 @@ namespace System.Globalization
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY);
- bool result = Interop.GlobalizationInterop.GetLocaleTimeFormat(_sWindowsName, shortFormat, sb, sb.Capacity);
+ bool result = Interop.Globalization.GetLocaleTimeFormat(_sWindowsName, shortFormat, sb, sb.Capacity);
if (!result)
{
// Failed, just use empty string
@@ -365,7 +365,7 @@ namespace System.Globalization
return Array.Empty<CultureInfo>();
}
- int bufferLength = Interop.GlobalizationInterop.GetLocales(null, 0);
+ int bufferLength = Interop.Globalization.GetLocales(null, 0);
if (bufferLength <= 0)
{
return Array.Empty<CultureInfo>();
@@ -373,7 +373,7 @@ namespace System.Globalization
Char [] chars = new Char[bufferLength];
- bufferLength = Interop.GlobalizationInterop.GetLocales(chars, bufferLength);
+ bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
if (bufferLength <= 0)
{
return Array.Empty<CultureInfo>();
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs
index 373fd26a8..393f983bb 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs
@@ -7,7 +7,6 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
-
using Internal.Runtime.CompilerServices;
#if ENABLE_WINRT
@@ -59,7 +58,7 @@ namespace System.Globalization
/// </summary>
private unsafe bool InitCultureData()
{
- const int LOCALE_NAME_MAX_LENGTH = 85;
+ Debug.Assert(!GlobalizationMode.Invariant);
const uint LOCALE_ILANGUAGE = 0x00000001;
const uint LOCALE_INEUTRAL = 0x00000071;
@@ -80,7 +79,7 @@ namespace System.Globalization
// It worked, note that the name is the locale name, so use that (even for neutrals)
// We need to clean up our "real" name, which should look like the windows name right now
// so overwrite the input with the cleaned up name
- _sRealName = new String(pBuffer, 0, result - 1);
+ _sRealName = new string(pBuffer, 0, result - 1);
realNameBuffer = _sRealName;
// Check for neutrality, don't expect to fail
@@ -97,8 +96,8 @@ namespace System.Globalization
// Note: Parents will be set dynamically
- // Start by assuming the windows name'll be the same as the specific name since windows knows
- // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
+ // Start by assuming the windows name will be the same as the specific name since windows knows
+ // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
_sWindowsName = realNameBuffer;
// Neutrals and non-neutrals are slightly different
@@ -120,7 +119,7 @@ namespace System.Globalization
}
// We found a locale name, so use it.
// In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
- _sSpecificCulture = new String(pBuffer, 0, result - 1);
+ _sSpecificCulture = new string(pBuffer, 0, result - 1);
}
else
{
@@ -163,8 +162,8 @@ namespace System.Globalization
}
// Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned
- // data as either and Int or String.
- internal static unsafe String GetLocaleInfoEx(String localeName, uint field)
+ // data as either and Int or string.
+ internal static unsafe string GetLocaleInfoEx(string localeName, uint field)
{
// REVIEW: Determine the maximum size for the buffer
const int BUFFER_SIZE = 530;
@@ -173,22 +172,22 @@ namespace System.Globalization
int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE);
if (resultCode > 0)
{
- return new String(pBuffer);
+ return new string(pBuffer);
}
return null;
}
- internal static unsafe int GetLocaleInfoExInt(String localeName, uint field)
+ internal static unsafe int GetLocaleInfoExInt(string localeName, uint field)
{
const uint LOCALE_RETURN_NUMBER = 0x20000000;
field |= LOCALE_RETURN_NUMBER;
int value = 0;
- GetLocaleInfoEx(localeName, field, (char*)&value, sizeof(int));
+ GetLocaleInfoEx(localeName, field, (char*) &value, sizeof(int));
return value;
}
- internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, void* lpLCData, int cchData)
+ internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData)
{
Debug.Assert(!GlobalizationMode.Invariant);
@@ -223,7 +222,7 @@ namespace System.Globalization
// Ask OS for data, note that we presume it returns success, so we have to know that
// sWindowsName is valid before calling.
Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
- return GetLocaleInfoExInt(_sWindowsName, lctype);
+ return GetLocaleInfoExInt(_sWindowsName, lctype);
}
private int[] GetLocaleInfo(LocaleGroupingData type)
@@ -250,28 +249,29 @@ namespace System.Globalization
return ConvertFirstDayOfWeekMonToSun(result);
}
- private String[] GetTimeFormats()
+ private string[] GetTimeFormats()
{
// Note that this gets overrides for us all the time
Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already");
- String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride));
+ string[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride));
return result;
}
- private String[] GetShortTimeFormats()
+ private string[] GetShortTimeFormats()
{
// Note that this gets overrides for us all the time
Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already");
- String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, TIME_NOSECONDS, UseUserOverride));
+ string[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, TIME_NOSECONDS, UseUserOverride));
return result;
}
- // Enumerate all system cultures and then try to find out which culture has
+ // Enumerate all system cultures and then try to find out which culture has
// region name match the requested region name
- private static CultureData GetCultureDataFromRegionName(String regionName)
+ private static CultureData GetCultureDataFromRegionName(string regionName)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
Debug.Assert(regionName != null);
const uint LOCALE_SUPPLEMENTAL = 0x00000002;
@@ -322,20 +322,14 @@ namespace System.Globalization
#if ENABLE_WINRT
return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode);
#else
- // Usually the UI culture shouldn't be different than what we got from WinRT except
- // if DefaultThreadCurrentUICulture was set
- CultureInfo ci;
-
- if (CultureInfo.DefaultThreadCurrentUICulture != null &&
- ((ci = GetUserDefaultCulture()) != null) &&
- !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
- {
- return SNATIVECOUNTRY;
- }
- else
+ // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
+ // otherwise, we use the native name as we don't carry resources for the region display names anyway.
+ if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
{
return GetLocaleInfo(LocaleStringData.LocalizedCountryName);
}
+
+ return SNATIVECOUNTRY;
#endif // ENABLE_WINRT
}
@@ -365,7 +359,7 @@ namespace System.Globalization
if (result == null)
{
// Failed, just use empty string
- result = String.Empty;
+ result = string.Empty;
}
return result;
@@ -385,7 +379,7 @@ namespace System.Globalization
//
// We don't build the stringbuilder unless we find something to change
////////////////////////////////////////////////////////////////////////////
- internal static String ReescapeWin32String(String str)
+ internal static string ReescapeWin32String(string str)
{
// If we don't have data, then don't try anything
if (str == null)
@@ -451,7 +445,7 @@ namespace System.Globalization
return result.ToString();
}
- internal static String[] ReescapeWin32Strings(String[] array)
+ internal static string[] ReescapeWin32Strings(string[] array)
{
if (array != null)
{
@@ -467,7 +461,7 @@ namespace System.Globalization
// If we get a group from windows, then its in 3;0 format with the 0 backwards
// of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
// EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
- private static int[] ConvertWin32GroupString(String win32Str)
+ private static int[] ConvertWin32GroupString(string win32Str)
{
// None of these cases make any sense
if (win32Str == null || win32Str.Length == 0)
@@ -552,6 +546,7 @@ namespace System.Globalization
}
}
+ // EnumSystemLocaleEx callback.
// [NativeCallable(CallingConvention = CallingConvention.StdCall)]
private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
{
@@ -570,7 +565,7 @@ namespace System.Globalization
// Context for EnumTimeFormatsEx callback.
private struct EnumData
{
- public LowLevelList<string> strings;
+ public List<string> strings;
}
// EnumTimeFormatsEx callback itself.
@@ -589,13 +584,13 @@ namespace System.Globalization
}
}
- private static unsafe String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride)
+ private static unsafe string[] nativeEnumTimeFormats(string localeName, uint dwFlags, bool useUserOverride)
{
const uint LOCALE_SSHORTTIME = 0x00000079;
const uint LOCALE_STIMEFORMAT = 0x00001003;
EnumData data = new EnumData();
- data.strings = new LowLevelList<string>();
+ data.strings = new List<string>();
// Now call the enumeration API. Work is done by our callback function
Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, Unsafe.AsPointer(ref data));
@@ -648,7 +643,7 @@ namespace System.Globalization
if (length > 0)
{
- return new String(pBuffer);
+ return new string(pBuffer);
}
return null;
@@ -723,14 +718,14 @@ namespace System.Globalization
}
EnumData context = new EnumData();
- context.strings = new LowLevelList<string>();
+ context.strings = new List<string>();
unsafe
{
Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, Unsafe.AsPointer(ref context), IntPtr.Zero);
}
- CultureInfo[] cultures = new CultureInfo[context.strings.Count];
+ CultureInfo [] cultures = new CultureInfo[context.strings.Count];
for (int i = 0; i < cultures.Length; i++)
{
cultures[i] = new CultureInfo(context.strings[i]);
@@ -759,16 +754,16 @@ namespace System.Globalization
get
{
EnumData context = new EnumData();
- context.strings = new LowLevelList<string>();
+ context.strings = new List<string>();
unsafe
{
Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, Unsafe.AsPointer(ref context), IntPtr.Zero);
}
- for (int i = 0; i < context.strings.Count; i++)
+ for (int i=0; i<context.strings.Count; i++)
{
- if (String.Compare(context.strings[i], _sWindowsName, StringComparison.OrdinalIgnoreCase) == 0)
+ if (string.Compare(context.strings[i], _sWindowsName, StringComparison.OrdinalIgnoreCase) == 0)
return true;
}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.cs
index e17d4b0ca..fda239c51 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureData.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.cs
@@ -9,6 +9,16 @@ using System.Threading;
namespace System.Globalization
{
+#if CORERT
+ using StringStringDictionary = LowLevelDictionary<string, string>;
+ using StringCultureDataDictionary = LowLevelDictionary<string, CultureData>;
+ using LcidToCultureNameDictionary = LowLevelDictionary<int, string>;
+#else
+ using StringStringDictionary = Dictionary<string, string>;
+ using StringCultureDataDictionary = Dictionary<string, CultureData>;
+ using LcidToCultureNameDictionary = Dictionary<int, string>;
+#endif
+
//
// List of culture data
// Note the we cache overrides.
@@ -42,82 +52,83 @@ namespace System.Globalization
//
internal partial class CultureData
{
+ private const int LOCALE_NAME_MAX_LENGTH = 85;
private const int undef = -1;
// Override flag
- private String _sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
- private String _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
+ private string _sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
+ private string _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
// Identity
- private String _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
- private String _sParent; // Parent name (which may be a custom locale/culture)
- private String _sLocalizedDisplayName; // Localized pretty name for this locale
- private String _sEnglishDisplayName; // English pretty name for this locale
- private String _sNativeDisplayName; // Native pretty name for this locale
- private String _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
+ private string _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
+ private string _sParent; // Parent name (which may be a custom locale/culture)
+ private string _sLocalizedDisplayName; // Localized pretty name for this locale
+ private string _sEnglishDisplayName; // English pretty name for this locale
+ private string _sNativeDisplayName; // Native pretty name for this locale
+ private string _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
// Language
- private String _sISO639Language; // ISO 639 Language Name
- private String _sISO639Language2; // ISO 639 Language Name
- private String _sLocalizedLanguage; // Localized name for this language
- private String _sEnglishLanguage; // English name for this language
- private String _sNativeLanguage; // Native name of this language
- private String _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
+ private string _sISO639Language; // ISO 639 Language Name
+ private string _sISO639Language2; // ISO 639 Language Name
+ private string _sLocalizedLanguage; // Localized name for this language
+ private string _sEnglishLanguage; // English name for this language
+ private string _sNativeLanguage; // Native name of this language
+ private string _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
private string _sConsoleFallbackName; // The culture name for the console fallback UI culture
- private int _iInputLanguageHandle = undef;// input language handle
+ private int _iInputLanguageHandle=undef;// input language handle
// Region
- private String _sRegionName; // (RegionInfo)
- private String _sLocalizedCountry; // localized country name
- private String _sEnglishCountry; // english country name (RegionInfo)
- private String _sNativeCountry; // native country name
- private String _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
- private String _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
- private int _iGeoId = undef; // GeoId
+ private string _sRegionName; // (RegionInfo)
+ private string _sLocalizedCountry; // localized country name
+ private string _sEnglishCountry; // english country name (RegionInfo)
+ private string _sNativeCountry; // native country name
+ private string _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
+ private string _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
+ private int _iGeoId = undef; // GeoId
// Numbers
- private String _sPositiveSign; // (user can override) positive sign
- private String _sNegativeSign; // (user can override) negative sign
+ private string _sPositiveSign; // (user can override) positive sign
+ private string _sNegativeSign; // (user can override) negative sign
// (nfi populates these 5, don't have to be = undef)
private int _iDigits; // (user can override) number of fractional digits
private int _iNegativeNumber; // (user can override) negative number format
private int[] _waGrouping; // (user can override) grouping of digits
- private String _sDecimalSeparator; // (user can override) decimal separator
- private String _sThousandSeparator; // (user can override) thousands separator
- private String _sNaN; // Not a Number
- private String _sPositiveInfinity; // + Infinity
- private String _sNegativeInfinity; // - Infinity
+ private string _sDecimalSeparator; // (user can override) decimal separator
+ private string _sThousandSeparator; // (user can override) thousands separator
+ private string _sNaN; // Not a Number
+ private string _sPositiveInfinity; // + Infinity
+ private string _sNegativeInfinity; // - Infinity
// Percent
private int _iNegativePercent = undef; // Negative Percent (0-3)
private int _iPositivePercent = undef; // Positive Percent (0-11)
- private String _sPercent; // Percent (%) symbol
- private String _sPerMille; // PerMille symbol
+ private string _sPercent; // Percent (%) symbol
+ private string _sPerMille; // PerMille symbol
// Currency
- private String _sCurrency; // (user can override) local monetary symbol
- private String _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
- private String _sEnglishCurrency; // English name for this currency
- private String _sNativeCurrency; // Native name for this currency
+ private string _sCurrency; // (user can override) local monetary symbol
+ private string _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
+ private string _sEnglishCurrency; // English name for this currency
+ private string _sNativeCurrency; // Native name for this currency
// (nfi populates these 4, don't have to be = undef)
private int _iCurrencyDigits; // (user can override) # local monetary fractional digits
private int _iCurrency; // (user can override) positive currency format
private int _iNegativeCurrency; // (user can override) negative currency format
private int[] _waMonetaryGrouping; // (user can override) monetary grouping of digits
- private String _sMonetaryDecimal; // (user can override) monetary decimal separator
- private String _sMonetaryThousand; // (user can override) monetary thousands separator
+ private string _sMonetaryDecimal; // (user can override) monetary decimal separator
+ private string _sMonetaryThousand; // (user can override) monetary thousands separator
// Misc
private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
- private String _sListSeparator; // (user can override) list separator
+ private string _sListSeparator; // (user can override) list separator
// Time
- private String _sAM1159; // (user can override) AM designator
- private String _sPM2359; // (user can override) PM designator
- private String _sTimeSeparator;
- private volatile String[] _saLongTimes; // (user can override) time format
- private volatile String[] _saShortTimes; // short time format
- private volatile String[] _saDurationFormats; // time duration format
+ private string _sAM1159; // (user can override) AM designator
+ private string _sPM2359; // (user can override) PM designator
+ private string _sTimeSeparator;
+ private volatile string[] _saLongTimes; // (user can override) time format
+ private volatile string[] _saShortTimes; // short time format
+ private volatile string[] _saDurationFormats; // time duration format
// Calendar specific data
private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
@@ -145,18 +156,17 @@ namespace System.Globalization
private bool _bUseOverrides; // use user overrides?
private bool _bNeutral; // Flags for the culture (ie: neutral or not right now)
-
// Region Name to Culture Name mapping table
// (In future would be nice to be in registry or something)
- //Using a property so we avoid creating the dictionary untill we need it
- private static LowLevelDictionary<string, string> RegionNames
+ //Using a property so we avoid creating the dictionary until we need it
+ private static StringStringDictionary RegionNames
{
get
{
if (s_RegionNames == null)
{
- LowLevelDictionary<string, string> regionNames = new LowLevelDictionary<string, string>(211 /* prime */);
+ StringStringDictionary regionNames = new StringStringDictionary(211 /* prime */);
regionNames.Add("029", "en-029");
regionNames.Add("AE", "ar-AE");
@@ -295,13 +305,13 @@ namespace System.Globalization
}
// Cache of regions we've already looked up
- private static volatile LowLevelDictionary<String, CultureData> s_cachedRegions;
- private static volatile LowLevelDictionary<string, string> s_RegionNames;
+ private static volatile StringCultureDataDictionary s_cachedRegions;
+ private static volatile StringStringDictionary s_RegionNames;
- internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
+ internal static CultureData GetCultureDataForRegion(string cultureName, bool useUserOverride)
{
// First do a shortcut for Invariant
- if (String.IsNullOrEmpty(cultureName))
+ if (string.IsNullOrEmpty(cultureName))
{
return CultureData.Invariant;
}
@@ -321,17 +331,17 @@ namespace System.Globalization
CultureData neutral = retVal;
// Try the hash table next
- String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
- LowLevelDictionary<String, CultureData> tempHashTable = s_cachedRegions;
+ string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
+ StringCultureDataDictionary tempHashTable = s_cachedRegions;
if (tempHashTable == null)
{
// No table yet, make a new one
- tempHashTable = new LowLevelDictionary<String, CultureData>();
+ tempHashTable = new StringCultureDataDictionary();
}
else
{
// Check the hash table
- using (LockHolder.Hold(s_lock))
+ lock (s_lock)
{
tempHashTable.TryGetValue(hashName, out retVal);
}
@@ -358,7 +368,7 @@ namespace System.Globalization
}
// If not found in the hard coded table we'll have to find a culture that works for us
- if (retVal == null || (retVal.IsNeutralCulture == true))
+ if (!GlobalizationMode.Invariant && (retVal == null || (retVal.IsNeutralCulture == true)))
{
retVal = GetCultureDataFromRegionName(cultureName);
}
@@ -367,7 +377,7 @@ namespace System.Globalization
if (retVal != null && (retVal.IsNeutralCulture == false))
{
// first add it to the cache
- using (LockHolder.Hold(s_lock))
+ lock (s_lock)
{
tempHashTable[hashName] = retVal;
}
@@ -410,7 +420,6 @@ namespace System.Globalization
// We have deprecated CultureTypes.FrameworkCultures.
// When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
- //
// We have deprecated CultureTypes.WindowsOnlyCultures.
// When this enum is used, we will return an empty array for this enum.
@@ -420,10 +429,125 @@ namespace System.Globalization
types &= (~CultureTypes.WindowsOnlyCultures);
}
+ if (GlobalizationMode.Invariant)
+ {
+ // in invariant mode we always return invariant culture only from the enumeration
+ return new CultureInfo[1] { new CultureInfo("") };
+ }
+
#pragma warning restore 618
return EnumCultures(types);
}
+ private static CultureData CreateCultureWithInvariantData()
+ {
+ // Make a new culturedata
+ CultureData invariant = new CultureData();
+
+ // Basics
+ // Note that we override the resources since this IS NOT supposed to change (by definition)
+ invariant._bUseOverrides = false;
+ invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
+ invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
+
+ // Identity
+ invariant._sName = ""; // locale name (ie: en-us)
+ invariant._sParent = ""; // Parent name (which may be a custom locale/culture)
+ invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now)
+ invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
+ invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
+ invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
+
+ // Language
+ invariant._sISO639Language = "iv"; // ISO 639 Language Name
+ invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
+ invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language
+ invariant._sEnglishLanguage = "Invariant Language"; // English name for this language
+ invariant._sNativeLanguage = "Invariant Language"; // Native name of this language
+ invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
+ invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
+ invariant._iInputLanguageHandle = 0x07F; // input language handle
+
+ // Region
+ invariant._sRegionName = "IV"; // (RegionInfo)
+ invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
+ invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only)
+ invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US
+ invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
+ invariant._iGeoId = 244; // GeoId (Windows Only)
+
+ // Numbers
+ invariant._sPositiveSign = "+"; // positive sign
+ invariant._sNegativeSign = "-"; // negative sign
+ invariant._iDigits = 2; // number of fractional digits
+ invariant._iNegativeNumber = 1; // negative number format
+ invariant._waGrouping = new int[] { 3 }; // grouping of digits
+ invariant._sDecimalSeparator = "."; // decimal separator
+ invariant._sThousandSeparator = ","; // thousands separator
+ invariant._sNaN = "NaN"; // Not a Number
+ invariant._sPositiveInfinity = "Infinity"; // + Infinity
+ invariant._sNegativeInfinity = "-Infinity"; // - Infinity
+
+ // Percent
+ invariant._iNegativePercent = 0; // Negative Percent (0-3)
+ invariant._iPositivePercent = 0; // Positive Percent (0-11)
+ invariant._sPercent = "%"; // Percent (%) symbol
+ invariant._sPerMille = "\x2030"; // PerMille symbol
+
+ // Currency
+ invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol
+ invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
+ invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
+ invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
+ invariant._iCurrencyDigits = 2; // # local monetary fractional digits
+ invariant._iCurrency = 0; // positive currency format
+ invariant._iNegativeCurrency = 0; // negative currency format
+ invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
+ invariant._sMonetaryDecimal = "."; // monetary decimal separator
+ invariant._sMonetaryThousand = ","; // monetary thousands separator
+
+ // Misc
+ invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
+ invariant._sListSeparator = ","; // list separator
+
+ // Time
+ invariant._sTimeSeparator = ":";
+ invariant._sAM1159 = "AM"; // AM designator
+ invariant._sPM2359 = "PM"; // PM designator
+ invariant._saLongTimes = new string[] { "HH:mm:ss" }; // time format
+ invariant._saShortTimes = new string[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
+ invariant._saDurationFormats = new string[] { "HH:mm:ss" }; // time duration format
+
+
+ // Calendar specific data
+ invariant._iFirstDayOfWeek = 0; // first day of week
+ invariant._iFirstWeekOfYear = 0; // first week of year
+ invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
+
+ // Store for specific data about each calendar
+ invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
+ invariant._calendars[0] = CalendarData.Invariant;
+
+ // Text information
+ invariant._iReadingLayout = 0;
+
+ // These are desktop only, not coreclr
+
+ invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information
+ invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
+ invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
+ invariant._iDefaultMacCodePage = 10000; // default macintosh code page
+ invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
+
+ if (GlobalizationMode.Invariant)
+ {
+ invariant._sLocalizedDisplayName = invariant._sNativeDisplayName;
+ invariant._sLocalizedCountry = invariant._sNativeCountry;
+ }
+
+ return invariant;
+ }
+
/////////////////////////////////////////////////////////////////////////
// Build our invariant information
//
@@ -435,104 +559,8 @@ namespace System.Globalization
{
if (s_Invariant == null)
{
- // Make a new culturedata
- CultureData invariant = new CultureData();
-
- // Basics
- // Note that we override the resources since this IS NOT supposed to change (by definition)
- invariant._bUseOverrides = false;
- invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
- invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
-
- // Identity
- invariant._sName = ""; // locale name (ie: en-us)
- invariant._sParent = ""; // Parent name (which may be a custom locale/culture)
- invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now)
- invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
- invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
- invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
-
- // Language
- invariant._sISO639Language = "iv"; // ISO 639 Language Name
- invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
- invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language
- invariant._sEnglishLanguage = "Invariant Language"; // English name for this language
- invariant._sNativeLanguage = "Invariant Language"; // Native name of this language
- invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
- invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
- invariant._iInputLanguageHandle = 0x07F; // input language handle
-
- // Region
- invariant._sRegionName = "IV"; // (RegionInfo)
- invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
- invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only)
- invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US
- invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
- invariant._iGeoId = 244; // GeoId (Windows Only)
-
- // Numbers
- invariant._sPositiveSign = "+"; // positive sign
- invariant._sNegativeSign = "-"; // negative sign
- invariant._iDigits = 2; // number of fractional digits
- invariant._iNegativeNumber = 1; // negative number format
- invariant._waGrouping = new int[] { 3 }; // grouping of digits
- invariant._sDecimalSeparator = "."; // decimal separator
- invariant._sThousandSeparator = ","; // thousands separator
- invariant._sNaN = "NaN"; // Not a Number
- invariant._sPositiveInfinity = "Infinity"; // + Infinity
- invariant._sNegativeInfinity = "-Infinity"; // - Infinity
-
- // Percent
- invariant._iNegativePercent = 0; // Negative Percent (0-3)
- invariant._iPositivePercent = 0; // Positive Percent (0-11)
- invariant._sPercent = "%"; // Percent (%) symbol
- invariant._sPerMille = "\x2030"; // PerMille symbol
-
- // Currency
- invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol
- invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
- invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
- invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
- invariant._iCurrencyDigits = 2; // # local monetary fractional digits
- invariant._iCurrency = 0; // positive currency format
- invariant._iNegativeCurrency = 0; // negative currency format
- invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
- invariant._sMonetaryDecimal = "."; // monetary decimal separator
- invariant._sMonetaryThousand = ","; // monetary thousands separator
-
- // Misc
- invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
- invariant._sListSeparator = ","; // list separator
-
- // Time
- invariant._sAM1159 = "AM"; // AM designator
- invariant._sPM2359 = "PM"; // PM designator
- invariant._saLongTimes = new String[] { "HH:mm:ss" }; // time format
- invariant._saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
- invariant._saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format
-
-
- // Calendar specific data
- invariant._iFirstDayOfWeek = 0; // first day of week
- invariant._iFirstWeekOfYear = 0; // first week of year
- invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
-
- // Store for specific data about each calendar
- invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
- invariant._calendars[0] = CalendarData.Invariant;
-
- // Text information
- invariant._iReadingLayout = 0;
-
- // These are desktop only, not coreclr
-
- invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information
- invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
- invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
- invariant._iDefaultMacCodePage = 10000; // default macintosh code page
- invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
// Remember it
- s_Invariant = invariant;
+ s_Invariant = CreateCultureWithInvariantData();
}
return s_Invariant;
}
@@ -543,31 +571,31 @@ namespace System.Globalization
// Constructors //
///////////////
// Cache of cultures we've already looked up
- private static volatile LowLevelDictionary<String, CultureData> s_cachedCultures;
- private static readonly Lock s_lock = new Lock();
+ private static volatile StringCultureDataDictionary s_cachedCultures;
+ private static readonly object s_lock = new object();
- internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
+ internal static CultureData GetCultureData(string cultureName, bool useUserOverride)
{
// First do a shortcut for Invariant
- if (String.IsNullOrEmpty(cultureName))
+ if (string.IsNullOrEmpty(cultureName))
{
return CultureData.Invariant;
}
// Try the hash table first
- String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
- LowLevelDictionary<String, CultureData> tempHashTable = s_cachedCultures;
+ string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
+ StringCultureDataDictionary tempHashTable = s_cachedCultures;
if (tempHashTable == null)
{
// No table yet, make a new one
- tempHashTable = new LowLevelDictionary<String, CultureData>();
+ tempHashTable = new StringCultureDataDictionary();
}
else
{
// Check the hash table
bool ret;
CultureData retVal;
- using (LockHolder.Hold(s_lock))
+ lock (s_lock)
{
ret = tempHashTable.TryGetValue(hashName, out retVal);
}
@@ -585,7 +613,7 @@ namespace System.Globalization
}
// Found one, add it to the cache
- using (LockHolder.Hold(s_lock))
+ lock (s_lock)
{
tempHashTable[hashName] = culture;
}
@@ -597,8 +625,80 @@ namespace System.Globalization
return culture;
}
+ private static string NormalizeCultureName(string name, out bool isNeutralName)
+ {
+ isNeutralName = true;
+ int i = 0;
+
+ if (name.Length > LOCALE_NAME_MAX_LENGTH)
+ {
+ // Theoretically we shouldn't hit this exception.
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidId, nameof(name)));
+ }
+
+ Span<char> normalizedName = stackalloc char[name.Length];
+
+ bool changed = false;
+
+ while (i < name.Length && name[i] != '-' && name[i] != '_')
+ {
+ if (name[i] >= 'A' && name[i] <= 'Z')
+ {
+ // lowercase characters before '-'
+ normalizedName[i] = (char) (((int)name[i]) + 0x20);
+ changed = true;
+ }
+ else
+ {
+ normalizedName[i] = name[i];
+ }
+ i++;
+ }
+
+ if (i < name.Length)
+ {
+ // this is not perfect to detect the non neutral cultures but it is good enough when we are running in invariant mode
+ isNeutralName = false;
+ }
+
+ while (i < name.Length)
+ {
+ if (name[i] >= 'a' && name[i] <= 'z')
+ {
+ normalizedName[i] = (char) (((int)name[i]) - 0x20);
+ changed = true;
+ }
+ else
+ {
+ normalizedName[i] = name[i];
+ }
+ i++;
+ }
+
+ if (changed)
+ return new string(normalizedName);
+
+ return name;
+ }
+
private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
{
+ if (GlobalizationMode.Invariant)
+ {
+ if (cultureName.Length > LOCALE_NAME_MAX_LENGTH || !CultureInfo.VerifyCultureName(cultureName, false))
+ {
+ return null;
+ }
+ CultureData cd = CreateCultureWithInvariantData();
+ cd._bUseOverrides = useUserOverride;
+ cd._sName = NormalizeCultureName(cultureName, out cd._bNeutral);
+ cd._sRealName = cd._sName;
+ cd._sWindowsName = cd._sName;
+ cd._iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
+
+ return cd;
+ }
+
CultureData culture = new CultureData();
culture._bUseOverrides = useUserOverride;
culture._sRealName = cultureName;
@@ -657,11 +757,17 @@ namespace System.Globalization
if (culture == CultureInfo.LOCALE_INVARIANT)
return Invariant;
+ if (GlobalizationMode.Invariant)
+ {
+ // LCID is not supported in the InvariantMode
+ throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
+ }
+
// Convert the lcid to a name, then use that
- // Note that this'll return neutral names (unlike Vista native API)
+ // Note that this will return neutral names (unlike Vista native API)
localeName = LCIDToLocaleName(culture);
- if (!String.IsNullOrEmpty(localeName))
+ if (!string.IsNullOrEmpty(localeName))
{
// Valid name, use it
retVal = GetCultureData(localeName, bUseUserOverride);
@@ -688,7 +794,7 @@ namespace System.Globalization
///////////
// The real name used to construct the locale (ie: de-DE_phoneb)
- internal String CultureName
+ internal string CultureName
{
get
{
@@ -716,20 +822,20 @@ namespace System.Globalization
}
// locale name (ie: de-DE, NO sort information)
- internal String SNAME
+ internal string SNAME
{
get
{
if (_sName == null)
{
- _sName = String.Empty;
+ _sName = string.Empty;
}
return _sName;
}
}
// Parent name (which may be a custom locale/culture)
- internal String SPARENT
+ internal string SPARENT
{
get
{
@@ -743,7 +849,7 @@ namespace System.Globalization
}
// Localized pretty name for this locale (ie: Inglis (estados Unitos))
- internal String SLOCALIZEDDISPLAYNAME
+ internal string SLOCALIZEDDISPLAYNAME
{
get
{
@@ -786,7 +892,7 @@ namespace System.Globalization
}
}
// If it hasn't been found (Windows 8 and up), fallback to the system
- if (String.IsNullOrEmpty(_sLocalizedDisplayName))
+ if (string.IsNullOrEmpty(_sLocalizedDisplayName))
{
// If its neutral use the language name
if (this.IsNeutralCulture)
@@ -795,7 +901,7 @@ namespace System.Globalization
}
else
{
- // Usually the UI culture shouldn't be different than what we got from WinRT except
+ // Usually the UI culture shouldn't be different than what we got from WinRT except
// if DefaultThreadCurrentUICulture was set
CultureInfo ci;
@@ -818,7 +924,7 @@ namespace System.Globalization
}
// English pretty name for this locale (ie: English (United States))
- internal String SENGDISPLAYNAME
+ internal string SENGDISPLAYNAME
{
get
{
@@ -842,7 +948,7 @@ namespace System.Globalization
_sEnglishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
// if it isn't found build one:
- if (String.IsNullOrEmpty(_sEnglishDisplayName))
+ if (string.IsNullOrEmpty(_sEnglishDisplayName))
{
// Our existing names mostly look like:
// "English" + "United States" -> "English (United States)"
@@ -867,7 +973,7 @@ namespace System.Globalization
}
// Native pretty name for this locale (ie: Deutsch (Deutschland))
- internal String SNATIVEDISPLAYNAME
+ internal string SNATIVEDISPLAYNAME
{
get
{
@@ -893,7 +999,7 @@ namespace System.Globalization
_sNativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
// if it isn't found build one:
- if (String.IsNullOrEmpty(_sNativeDisplayName))
+ if (string.IsNullOrEmpty(_sNativeDisplayName))
{
// These should primarily be "Deutsch (Deutschland)" type names
_sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
@@ -920,7 +1026,7 @@ namespace System.Globalization
/////////////
// iso 639 language name, ie: en
- internal String SISO639LANGNAME
+ internal string SISO639LANGNAME
{
get
{
@@ -960,13 +1066,13 @@ namespace System.Globalization
// Localized name for this language (Windows Only) ie: Inglis
// This is only valid for Windows 8 and higher neutrals:
- internal String SLOCALIZEDLANGUAGE
+ internal string SLOCALIZEDLANGUAGE
{
get
{
if (_sLocalizedLanguage == null)
{
- // Usually the UI culture shouldn't be different than what we got from WinRT except
+ // Usually the UI culture shouldn't be different than what we got from WinRT except
// if DefaultThreadCurrentUICulture was set
CultureInfo ci;
@@ -987,7 +1093,7 @@ namespace System.Globalization
}
// English name for this language (Windows Only) ie: German
- internal String SENGLISHLANGUAGE
+ internal string SENGLISHLANGUAGE
{
get
{
@@ -1000,7 +1106,7 @@ namespace System.Globalization
}
// Native name of this language (Windows Only) ie: Deutsch
- internal String SNATIVELANGUAGE
+ internal string SNATIVELANGUAGE
{
get
{
@@ -1017,7 +1123,7 @@ namespace System.Globalization
///////////
// region name (eg US)
- internal String SREGIONNAME
+ internal string SREGIONNAME
{
get
{
@@ -1054,7 +1160,7 @@ namespace System.Globalization
}
catch (Exception)
{
- // do nothing. we'll fallback
+ // do nothing. we'll fallback
}
if (_sLocalizedCountry == null)
@@ -1067,7 +1173,7 @@ namespace System.Globalization
}
// english country name (RegionInfo) ie: Germany
- internal String SENGCOUNTRY
+ internal string SENGCOUNTRY
{
get
{
@@ -1080,7 +1186,7 @@ namespace System.Globalization
}
// native country name (RegionInfo) ie: Deutschland
- internal String SNATIVECOUNTRY
+ internal string SNATIVECOUNTRY
{
get
{
@@ -1093,7 +1199,7 @@ namespace System.Globalization
}
// ISO 3166 Country Name
- internal String SISO3166CTRYNAME
+ internal string SISO3166CTRYNAME
{
get
{
@@ -1106,7 +1212,7 @@ namespace System.Globalization
}
// 3 letter ISO 3166 country code
- internal String SISO3166CTRYNAME2
+ internal string SISO3166CTRYNAME2
{
get
{
@@ -1165,11 +1271,11 @@ namespace System.Globalization
}
- // internal String sDecimalSeparator ; // (user can override) decimal separator
- // internal String sThousandSeparator ; // (user can override) thousands separator
+ // internal string _sDecimalSeparator ; // (user can override) decimal separator
+ // internal string _sThousandSeparator ; // (user can override) thousands separator
// Not a Number
- internal String SNAN
+ internal string SNAN
{
get
{
@@ -1182,7 +1288,7 @@ namespace System.Globalization
}
// + Infinity
- internal String SPOSINFINITY
+ internal string SPOSINFINITY
{
get
{
@@ -1195,7 +1301,7 @@ namespace System.Globalization
}
// - Infinity
- internal String SNEGINFINITY
+ internal string SNEGINFINITY
{
get
{
@@ -1241,7 +1347,7 @@ namespace System.Globalization
}
// Percent (%) symbol
- internal String SPERCENT
+ internal string SPERCENT
{
get
{
@@ -1254,7 +1360,7 @@ namespace System.Globalization
}
// PerMille symbol
- internal String SPERMILLE
+ internal string SPERMILLE
{
get
{
@@ -1271,7 +1377,7 @@ namespace System.Globalization
/////////////
// (user can override) local monetary symbol, eg: $
- internal String SCURRENCY
+ internal string SCURRENCY
{
get
{
@@ -1284,7 +1390,7 @@ namespace System.Globalization
}
// international monetary symbol (RegionInfo), eg: USD
- internal String SINTLSYMBOL
+ internal string SINTLSYMBOL
{
get
{
@@ -1297,7 +1403,7 @@ namespace System.Globalization
}
// English name for this currency (RegionInfo), eg: US Dollar
- internal String SENGLISHCURRENCY
+ internal string SENGLISHCURRENCY
{
get
{
@@ -1310,7 +1416,7 @@ namespace System.Globalization
}
// Native name for this currency (RegionInfo), eg: Schweiz Frank
- internal String SNATIVECURRENCY
+ internal string SNATIVECURRENCY
{
get
{
@@ -1353,7 +1459,7 @@ namespace System.Globalization
}
// (user can override) list Separator
- internal String SLIST
+ internal string SLIST
{
get
{
@@ -1371,7 +1477,7 @@ namespace System.Globalization
////////////////////////////
// (user can override) AM designator
- internal String SAM1159
+ internal string SAM1159
{
get
{
@@ -1384,7 +1490,7 @@ namespace System.Globalization
}
// (user can override) PM designator
- internal String SPM2359
+ internal string SPM2359
{
get
{
@@ -1397,13 +1503,15 @@ namespace System.Globalization
}
// (user can override) time format
- internal String[] LongTimes
+ internal string[] LongTimes
{
get
{
if (_saLongTimes == null)
{
- String[] longTimes = GetTimeFormats();
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ string[] longTimes = GetTimeFormats();
if (longTimes == null || longTimes.Length == 0)
{
_saLongTimes = Invariant._saLongTimes;
@@ -1420,14 +1528,16 @@ namespace System.Globalization
// short time format
// Short times (derived from long times format)
// TODO: NLS Arrowhead - On Windows 7 we should have short times so this isn't necessary
- internal String[] ShortTimes
+ internal string[] ShortTimes
{
get
{
if (_saShortTimes == null)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
// Try to get the short times from the OS/culture.dll
- String[] shortTimes = null;
+ string[] shortTimes = null;
shortTimes = GetShortTimeFormats();
if (shortTimes == null || shortTimes.Length == 0)
@@ -1463,11 +1573,12 @@ namespace System.Globalization
// between it and the previous marker, if any. If its a short, unescaped separator,
// then we don't retain that part.
// We then check after the ss and remove anything before the next h,H,m,t...
- string[] shortTimes = new string[LongTimes.Length];
+ string[] longTimes = LongTimes;
+ string[] shortTimes = new string[longTimes.Length];
- for (int i = 0; i < LongTimes.Length; i++)
+ for (int i = 0; i < longTimes.Length; i++)
{
- shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
+ shortTimes[i] = StripSecondsFromPattern(longTimes[i]);
}
return shortTimes;
}
@@ -1609,19 +1720,19 @@ namespace System.Globalization
}
// (user can override default only) short date format
- internal String[] ShortDates(CalendarId calendarId)
+ internal string[] ShortDates(CalendarId calendarId)
{
return GetCalendar(calendarId).saShortDates;
}
// (user can override default only) long date format
- internal String[] LongDates(CalendarId calendarId)
+ internal string[] LongDates(CalendarId calendarId)
{
return GetCalendar(calendarId).saLongDates;
}
// (user can override) date year/month format.
- internal String[] YearMonths(CalendarId calendarId)
+ internal string[] YearMonths(CalendarId calendarId)
{
return GetCalendar(calendarId).saYearMonths;
}
@@ -1678,7 +1789,7 @@ namespace System.Globalization
}
// month/day format (single string, no override)
- internal String MonthDay(CalendarId calendarId)
+ internal string MonthDay(CalendarId calendarId)
{
return GetCalendar(calendarId).sMonthDay;
}
@@ -1846,7 +1957,7 @@ namespace System.Globalization
// fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
// fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
// es-ES_tradnl -> es-ES
- internal String STEXTINFO // Text info name to use for text information
+ internal string STEXTINFO // Text info name to use for text information
{
get
{
@@ -1858,7 +1969,7 @@ namespace System.Globalization
}
// Compare info name (including sorting key) to use if custom
- internal String SCOMPAREINFO
+ internal string SCOMPAREINFO
{
get
{
@@ -1929,7 +2040,7 @@ namespace System.Globalization
{
if (_iLanguage == 0)
{
- Debug.Assert(_sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
+ Debug.Assert(_sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated already");
_iLanguage = LocaleNameToLCID(_sRealName);
}
return _iLanguage;
@@ -1949,7 +2060,7 @@ namespace System.Globalization
{
get
{
- return String.IsNullOrEmpty(this.SNAME);
+ return string.IsNullOrEmpty(this.SNAME);
}
}
@@ -1958,6 +2069,11 @@ namespace System.Globalization
{
get
{
+ if (GlobalizationMode.Invariant)
+ {
+ return CultureInfo.GetCalendarInstance(CalendarIds[0]);
+ }
+
CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
if (defaultCalId == 0)
@@ -1970,40 +2086,40 @@ namespace System.Globalization
}
// All of our era names
- internal String[] EraNames(CalendarId calendarId)
+ internal string[] EraNames(CalendarId calendarId)
{
Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
return this.GetCalendar(calendarId).saEraNames;
}
- internal String[] AbbrevEraNames(CalendarId calendarId)
+ internal string[] AbbrevEraNames(CalendarId calendarId)
{
Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
return this.GetCalendar(calendarId).saAbbrevEraNames;
}
- internal String[] AbbreviatedEnglishEraNames(CalendarId calendarId)
+ internal string[] AbbreviatedEnglishEraNames(CalendarId calendarId)
{
Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
}
- //// String array DEFAULTS
+ //// string array DEFAULTS
//// Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
// Time separator (derived from time format)
- internal String TimeSeparator
+ internal string TimeSeparator
{
get
{
if (_sTimeSeparator == null)
{
string longTimeFormat = GetTimeFormatString();
- if (String.IsNullOrEmpty(longTimeFormat))
+ if (string.IsNullOrEmpty(longTimeFormat))
{
longTimeFormat = LongTimes[0];
}
@@ -2016,7 +2132,7 @@ namespace System.Globalization
}
// Date separator (derived from short date format)
- internal String DateSeparator(CalendarId calendarId)
+ internal string DateSeparator(CalendarId calendarId)
{
return GetDateSeparator(ShortDates(calendarId)[0]);
}
@@ -2044,7 +2160,7 @@ namespace System.Globalization
// always build a stringbuilder because we need to remove the ' or \.
//
////////////////////////////////////////////////////////////////////////////
- private static String UnescapeNlsString(String str, int start, int end)
+ private static string UnescapeNlsString(string str, int start, int end)
{
Debug.Assert(str != null);
Debug.Assert(start >= 0);
@@ -2087,7 +2203,7 @@ namespace System.Globalization
return (result.ToString());
}
- private static String GetTimeSeparator(String format)
+ private static string GetTimeSeparator(string format)
{
// Time format separator (ie: : in 12:39:00)
//
@@ -2100,7 +2216,7 @@ namespace System.Globalization
return GetSeparator(format, "Hhms");
}
- private static String GetDateSeparator(String format)
+ private static string GetDateSeparator(string format)
{
// Date format separator (ie: / in 9/1/03)
//
@@ -2141,7 +2257,7 @@ namespace System.Globalization
}
}
- return String.Empty;
+ return string.Empty;
}
private static int IndexOfTimePart(string format, int startIndex, string timeParts)
@@ -2189,7 +2305,7 @@ namespace System.Globalization
internal void GetNFIValues(NumberFormatInfo nfi)
{
- if (this.IsInvariantCulture)
+ if (GlobalizationMode.Invariant || this.IsInvariantCulture)
{
// FUTURE: NumberFormatInfo already has default values for many of these fields. Can we not do this?
nfi.positiveSign = _sPositiveSign;
@@ -2284,7 +2400,7 @@ namespace System.Globalization
{
int index = 0;
- while (index < testString.Length && (testString[index] < 'A' || testString[index] > 'Z'))
+ while (index<testString.Length && (testString[index]<'A' || testString[index]>'Z' ))
{
index++;
}
@@ -2294,14 +2410,14 @@ namespace System.Globalization
}
StringBuilder sb = new StringBuilder(testString.Length);
- for (int i = 0; i < index; i++)
+ for (int i=0; i<index; i++)
{
sb.Append(testString[i]);
}
- sb.Append((char)(testString[index] - 'A' + 'a'));
+ sb.Append((char) (testString[index] -'A' + 'a'));
- for (int ich = index + 1; ich < testString.Length; ich++)
+ for (int ich = index+1; ich < testString.Length; ich++)
{
char ch = testString[ich];
sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
@@ -2316,77 +2432,77 @@ namespace System.Globalization
/// </remarks>
private enum LocaleStringData : uint
{
- /// <summary>localized name of locale, eg "German (Germany)" in UI language (coresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
+ /// <summary>localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
LocalizedDisplayName = 0x00000002,
- /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (coresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
+ /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
EnglishDisplayName = 0x00000072,
- /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (coresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
+ /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
NativeDisplayName = 0x00000073,
- /// <summary>Language Display Name for a language, eg "German" in UI language (coresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
+ /// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
LocalizedLanguageName = 0x0000006f,
- /// <summary>English name of language, eg "German" (coresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
+ /// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
EnglishLanguageName = 0x00001001,
- /// <summary>native name of language, eg "Deutsch" (coresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
+ /// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
NativeLanguageName = 0x00000004,
- /// <summary>localized name of country, eg "Germany" in UI language (coresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
+ /// <summary>localized name of country, eg "Germany" in UI language (corresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
LocalizedCountryName = 0x00000006,
- /// <summary>English name of country, eg "Germany" (coresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
+ /// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
EnglishCountryName = 0x00001002,
- /// <summary>native name of country, eg "Deutschland" (coresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
+ /// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
NativeCountryName = 0x00000008,
- /// <summary>abbreviated language name (coresponds to LOCALE_SABBREVLANGNAME)</summary>
+ /// <summary>abbreviated language name (corresponds to LOCALE_SABBREVLANGNAME)</summary>
AbbreviatedWindowsLanguageName = 0x00000003,
- /// <summary>list item separator (coresponds to LOCALE_SLIST)</summary>
+ /// <summary>list item separator (corresponds to LOCALE_SLIST)</summary>
ListSeparator = 0x0000000C,
- /// <summary>decimal separator (coresponds to LOCALE_SDECIMAL)</summary>
+ /// <summary>decimal separator (corresponds to LOCALE_SDECIMAL)</summary>
DecimalSeparator = 0x0000000E,
- /// <summary>thousand separator (coresponds to LOCALE_STHOUSAND)</summary>
+ /// <summary>thousand separator (corresponds to LOCALE_STHOUSAND)</summary>
ThousandSeparator = 0x0000000F,
- /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
Digits = 0x00000013,
- /// <summary>local monetary symbol (coresponds to LOCALE_SCURRENCY)</summary>
+ /// <summary>local monetary symbol (corresponds to LOCALE_SCURRENCY)</summary>
MonetarySymbol = 0x00000014,
- /// <summary>English currency name (coresponds to LOCALE_SENGCURRNAME)</summary>
+ /// <summary>English currency name (corresponds to LOCALE_SENGCURRNAME)</summary>
CurrencyEnglishName = 0x00001007,
- /// <summary>Native currency name (coresponds to LOCALE_SNATIVECURRNAME)</summary>
+ /// <summary>Native currency name (corresponds to LOCALE_SNATIVECURRNAME)</summary>
CurrencyNativeName = 0x00001008,
- /// <summary>uintl monetary symbol (coresponds to LOCALE_SINTLSYMBOL)</summary>
+ /// <summary>uintl monetary symbol (corresponds to LOCALE_SINTLSYMBOL)</summary>
Iso4217MonetarySymbol = 0x00000015,
- /// <summary>monetary decimal separator (coresponds to LOCALE_SMONDECIMALSEP)</summary>
+ /// <summary>monetary decimal separator (corresponds to LOCALE_SMONDECIMALSEP)</summary>
MonetaryDecimalSeparator = 0x00000016,
- /// <summary>monetary thousand separator (coresponds to LOCALE_SMONTHOUSANDSEP)</summary>
+ /// <summary>monetary thousand separator (corresponds to LOCALE_SMONTHOUSANDSEP)</summary>
MonetaryThousandSeparator = 0x00000017,
- /// <summary>AM designator (coresponds to LOCALE_S1159)</summary>
+ /// <summary>AM designator (corresponds to LOCALE_S1159)</summary>
AMDesignator = 0x00000028,
- /// <summary>PM designator (coresponds to LOCALE_S2359)</summary>
+ /// <summary>PM designator (corresponds to LOCALE_S2359)</summary>
PMDesignator = 0x00000029,
- /// <summary>positive sign (coresponds to LOCALE_SPOSITIVESIGN)</summary>
+ /// <summary>positive sign (corresponds to LOCALE_SPOSITIVESIGN)</summary>
PositiveSign = 0x00000050,
- /// <summary>negative sign (coresponds to LOCALE_SNEGATIVESIGN)</summary>
+ /// <summary>negative sign (corresponds to LOCALE_SNEGATIVESIGN)</summary>
NegativeSign = 0x00000051,
- /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
+ /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
Iso639LanguageTwoLetterName = 0x00000059,
- /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO639LANGNAME2)</summary>
+ /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO639LANGNAME2)</summary>
Iso639LanguageThreeLetterName = 0x00000067,
- /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
+ /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
Iso639LanguageName = 0x00000059,
- /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO3166CTRYNAME)</summary>
+ /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO3166CTRYNAME)</summary>
Iso3166CountryName = 0x0000005A,
- /// <summary>3 letter ISO country code (coresponds to LOCALE_SISO3166CTRYNAME2)</summary>
+ /// <summary>3 letter ISO country code (corresponds to LOCALE_SISO3166CTRYNAME2)</summary>
Iso3166CountryName2 = 0x00000068, // 3 character ISO country name
- /// <summary>Not a Number (coresponds to LOCALE_SNAN)</summary>
+ /// <summary>Not a Number (corresponds to LOCALE_SNAN)</summary>
NaNSymbol = 0x00000069,
- /// <summary>+ Infinity (coresponds to LOCALE_SPOSINFINITY)</summary>
+ /// <summary>+ Infinity (corresponds to LOCALE_SPOSINFINITY)</summary>
PositiveInfinitySymbol = 0x0000006a,
- /// <summary>- Infinity (coresponds to LOCALE_SNEGINFINITY)</summary>
+ /// <summary>- Infinity (corresponds to LOCALE_SNEGINFINITY)</summary>
NegativeInfinitySymbol = 0x0000006b,
- /// <summary>Fallback name for resources (coresponds to LOCALE_SPARENT)</summary>
+ /// <summary>Fallback name for resources (corresponds to LOCALE_SPARENT)</summary>
ParentName = 0x0000006d,
- /// <summary>Fallback name for within the console (coresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
+ /// <summary>Fallback name for within the console (corresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
ConsoleFallbackName = 0x0000006e,
- /// <summary>Returns the percent symbol (coresponds to LOCALE_SPERCENT)</summary>
+ /// <summary>Returns the percent symbol (corresponds to LOCALE_SPERCENT)</summary>
PercentSymbol = 0x00000076,
- /// <summary>Returns the permille (U+2030) symbol (coresponds to LOCALE_SPERMILLE)</summary>
+ /// <summary>Returns the permille (U+2030) symbol (corresponds to LOCALE_SPERMILLE)</summary>
PerMilleSymbol = 0x00000077
}
@@ -2396,9 +2512,9 @@ namespace System.Globalization
/// </remarks>
private enum LocaleGroupingData : uint
{
- /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
Digit = 0x00000010,
- /// <summary>monetary grouping (coresponds to LOCALE_SMONGROUPING)</summary>
+ /// <summary>monetary grouping (corresponds to LOCALE_SMONGROUPING)</summary>
Monetary = 0x00000018,
}
@@ -2408,29 +2524,29 @@ namespace System.Globalization
/// </remarks>
private enum LocaleNumberData : uint
{
- /// <summary>language id (coresponds to LOCALE_ILANGUAGE)</summary>
+ /// <summary>language id (corresponds to LOCALE_ILANGUAGE)</summary>
LanguageId = 0x00000001,
- /// <summary>geographical location id, (coresponds to LOCALE_IGEOID)</summary>
+ /// <summary>geographical location id, (corresponds to LOCALE_IGEOID)</summary>
GeoId = 0x0000005B,
- /// <summary>0 = context, 1 = none, 2 = national (coresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
+ /// <summary>0 = context, 1 = none, 2 = national (corresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
DigitSubstitution = 0x00001014,
- /// <summary>0 = metric, 1 = US (coresponds to LOCALE_IMEASURE)</summary>
+ /// <summary>0 = metric, 1 = US (corresponds to LOCALE_IMEASURE)</summary>
MeasurementSystem = 0x0000000D,
- /// <summary>number of fractional digits (coresponds to LOCALE_IDIGITS)</summary>
+ /// <summary>number of fractional digits (corresponds to LOCALE_IDIGITS)</summary>
FractionalDigitsCount = 0x00000011,
- /// <summary>negative number mode (coresponds to LOCALE_INEGNUMBER)</summary>
+ /// <summary>negative number mode (corresponds to LOCALE_INEGNUMBER)</summary>
NegativeNumberFormat = 0x00001010,
- /// <summary># local monetary digits (coresponds to LOCALE_ICURRDIGITS)</summary>
+ /// <summary># local monetary digits (corresponds to LOCALE_ICURRDIGITS)</summary>
MonetaryFractionalDigitsCount = 0x00000019,
- /// <summary>positive currency mode (coresponds to LOCALE_ICURRENCY)</summary>
+ /// <summary>positive currency mode (corresponds to LOCALE_ICURRENCY)</summary>
PositiveMonetaryNumberFormat = 0x0000001B,
- /// <summary>negative currency mode (coresponds to LOCALE_INEGCURR)</summary>
+ /// <summary>negative currency mode (corresponds to LOCALE_INEGCURR)</summary>
NegativeMonetaryNumberFormat = 0x0000001C,
- /// <summary>type of calendar specifier (coresponds to LOCALE_ICALENDARTYPE)</summary>
+ /// <summary>type of calendar specifier (corresponds to LOCALE_ICALENDARTYPE)</summary>
CalendarType = 0x00001009,
- /// <summary>first day of week specifier (coresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
+ /// <summary>first day of week specifier (corresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
FirstDayOfWeek = 0x0000100C,
- /// <summary>first week of year specifier (coresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
+ /// <summary>first week of year specifier (corresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
FirstWeekOfYear = 0x0000100D,
/// <summary>
/// Returns one of the following 4 reading layout values:
@@ -2438,20 +2554,20 @@ namespace System.Globalization
/// 1 - Right to left (eg arabic locales)
/// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
/// 3 - Vertical top to bottom with columns proceeding to the right
- /// (coresponds to LOCALE_IREADINGLAYOUT)
+ /// (corresponds to LOCALE_IREADINGLAYOUT)
/// </summary>
ReadingLayout = 0x00000070,
- /// <summary>Returns 0-11 for the negative percent format (coresponds to LOCALE_INEGATIVEPERCENT)</summary>
+ /// <summary>Returns 0-11 for the negative percent format (corresponds to LOCALE_INEGATIVEPERCENT)</summary>
NegativePercentFormat = 0x00000074,
- /// <summary>Returns 0-3 for the positive percent format (coresponds to LOCALE_IPOSITIVEPERCENT)</summary>
+ /// <summary>Returns 0-3 for the positive percent format (corresponds to LOCALE_IPOSITIVEPERCENT)</summary>
PositivePercentFormat = 0x00000075,
- /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
+ /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
OemCodePage = 0x0000000B,
- /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
+ /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
AnsiCodePage = 0x00001004,
- /// <summary>default mac code page (coresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
+ /// <summary>default mac code page (corresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
MacCodePage = 0x00001011,
- /// <summary>default ebcdic code page (coresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
+ /// <summary>default ebcdic code page (corresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
EbcdicCodePage = 0x00001012,
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs
index d15cc1cc8..595fb5631 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
+using System.Runtime.InteropServices;
namespace System
{
@@ -703,7 +704,7 @@ namespace System
if (nextChar >= 0 && nextChar != '%')
{
char nextCharChar = (char)nextChar;
- StringBuilder origStringBuilder = FormatCustomized(dateTime, ReadOnlySpan<char>.DangerousCreate(null, ref nextCharChar, 1), dtfi, offset, result);
+ StringBuilder origStringBuilder = FormatCustomized(dateTime, MemoryMarshal.CreateReadOnlySpan<char>(ref nextCharChar, 1), dtfi, offset, result);
Debug.Assert(ReferenceEquals(origStringBuilder, result));
tokenLen = 2;
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
index b6a462150..faa0deac3 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
@@ -593,7 +593,7 @@ namespace System.Globalization
{
// this comparison should use the InvariantCulture. The English name could have linguistically
// interesting characters.
- if (CultureInfo.InvariantCulture.CompareInfo.Compare(eraName, m_abbrevEnglishEraNames[i], CompareOptions.IgnoreCase) == 0)
+ if (CompareInfo.Invariant.Compare(eraName, m_abbrevEnglishEraNames[i], CompareOptions.IgnoreCase) == 0)
{
return (i + 1);
}
@@ -1463,7 +1463,7 @@ namespace System.Globalization
//
// Actions: Retrieve the month names used in a leap year.
// If this culture does not have different month names in a leap year, the normal month name is returned.
- // Agruments: None. (can use abbreviated later if needed)
+ // Arguments: None. (can use abbreviated later if needed)
//
internal String[] internalGetLeapYearMonthNames(/*bool abbreviated*/)
{
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs
index c38e7a26b..d3c3aac84 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs
@@ -24,14 +24,6 @@ using System.Text;
namespace System.Globalization
{
-#if CORECLR
- using StringStringDictionary = Dictionary<string, string>;
- using StringList = List<string>;
-#else
- using StringStringDictionary = LowLevelDictionary<string, string>;
- using StringList = LowLevelList<string>;
-#endif
-
//
// from LocaleEx.txt header
//
@@ -121,17 +113,17 @@ namespace System.Globalization
internal const String CJKSecondSuff = "\u79d2";
// The collection fo date words & postfix.
- internal StringList m_dateWords = new StringList();
+ internal List<string> m_dateWords = new List<string>();
// Hashtable for the known words.
- private static volatile StringStringDictionary s_knownWords;
+ private static volatile Dictionary<string, string> s_knownWords;
- static StringStringDictionary KnownWords
+ static Dictionary<string, string> KnownWords
{
get
{
if (s_knownWords == null)
{
- StringStringDictionary temp = new StringStringDictionary();
+ Dictionary<string, string> temp = new Dictionary<string, string>();
// Add known words into the hash table.
// Skip these special symbols.
@@ -234,7 +226,7 @@ namespace System.Globalization
{
if (m_dateWords == null)
{
- m_dateWords = new StringList();
+ m_dateWords = new List<string>();
}
if (formatPostfix == "MMMM")
{
@@ -380,7 +372,7 @@ namespace System.Globalization
if (m_dateWords == null)
{
// Create the date word array.
- m_dateWords = new StringList();
+ m_dateWords = new List<string>();
}
// Add the ignorable symbol into the ArrayList.
String temp = IgnorableSymbolChar + text;
@@ -735,4 +727,3 @@ namespace System.Globalization
}
}
}
-
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
index cbf90945c..5b285eb5d 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
@@ -1200,7 +1200,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else if ((!Char.IsWhiteSpace(ch)))
{
- // Anthyhing other than whitespace outside hashes is invalid
+ // Anything other than whitespace outside hashes is invalid
if (!foundStart || foundEnd)
{
return false;
@@ -2108,7 +2108,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
if (result.Year != -1)
{
- // Aleady has a year suffix
+ // Already has a year suffix
result.SetBadDateTimeFailure();
return false;
}
@@ -2702,7 +2702,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
//
// We have to check day of week before we adjust to the time zone.
- // Otherwise, the value of day of week may change after adjustting to the time zone.
+ // Otherwise, the value of day of week may change after adjusting to the time zone.
//
if (raw.dayOfWeek != -1)
{
@@ -2860,7 +2860,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
// The constructor should always succeed because of the range check earlier in the function
- // Althought it is UTC, internally DateTimeOffset does not use this flag
+ // Although it is UTC, internally DateTimeOffset does not use this flag
result.parsedDate = new DateTime(utcTicks, DateTimeKind.Utc);
result.timeZoneOffset = TimeSpan.Zero;
}
@@ -3607,15 +3607,18 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// change the code below.
if (str.GetNext())
{
- if (str.GetChar() == dtfi.AMDesignator[0])
+ string amDesignator = dtfi.AMDesignator;
+ if (amDesignator.Length > 0 && str.GetChar() == amDesignator[0])
{
result = TM.AM;
- return (true);
+ return true;
}
- if (str.GetChar() == dtfi.PMDesignator[0])
+
+ string pmDesignator = dtfi.PMDesignator;
+ if (pmDesignator.Length > 0 && str.GetChar() == pmDesignator[0])
{
result = TM.PM;
- return (true);
+ return true;
}
}
return false;
@@ -5042,7 +5045,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
return false;
}
- if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsReadOnlySpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
+ if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
{
return false;
}
@@ -5068,7 +5071,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
return false;
}
- if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsReadOnlySpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
+ if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
{
return false;
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeStyles.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeStyles.cs
index 79232ff19..bf68ac91d 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeStyles.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeStyles.cs
@@ -36,7 +36,7 @@ namespace System.Globalization
NoCurrentDateDefault = 0x00000008,
// When parsing a date/time string, if a timezone specifier ("GMT","Z","+xxxx", "-xxxx" exists), we will
- // ajdust the parsed time based to GMT.
+ // adjust the parsed time based to GMT.
AdjustToUniversal = 0x00000010,
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/GlobalizationExtensions.cs b/src/System.Private.CoreLib/shared/System/Globalization/GlobalizationExtensions.cs
new file mode 100644
index 000000000..007283aa6
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Globalization/GlobalizationExtensions.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+
+namespace System.Globalization
+{
+ public static class GlobalizationExtensions
+ {
+ public static StringComparer GetStringComparer(this CompareInfo compareInfo, CompareOptions options)
+ {
+ if (compareInfo == null)
+ {
+ throw new ArgumentNullException(nameof(compareInfo));
+ }
+
+ if (options == CompareOptions.Ordinal)
+ {
+ return StringComparer.Ordinal;
+ }
+
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
+ return StringComparer.OrdinalIgnoreCase;
+ }
+
+ return new CultureAwareComparer(compareInfo, options);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs b/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs
index 09b1f20c4..365942cce 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs
@@ -69,7 +69,7 @@ namespace System.Globalization
{
try
{
- int advance = Int32.Parse(str.AsReadOnlySpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture);
+ int advance = Int32.Parse(str.AsSpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture);
if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
{
hijriAdvance = advance;
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Unix.cs
index 2bbda0d3a..5320936a7 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Unix.cs
@@ -23,7 +23,7 @@ namespace System.Globalization
if (estimatedLength < StackallocThreshold)
{
char* outputStack = stackalloc char[estimatedLength];
- actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, outputStack, estimatedLength);
+ actualLength = Interop.Globalization.ToAscii(flags, unicode, count, outputStack, estimatedLength);
if (actualLength > 0 && actualLength <= estimatedLength)
{
return new string(outputStack, 0, actualLength);
@@ -31,7 +31,7 @@ namespace System.Globalization
}
else
{
- actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, null, 0);
+ actualLength = Interop.Globalization.ToAscii(flags, unicode, count, null, 0);
}
if (actualLength == 0)
{
@@ -41,7 +41,7 @@ namespace System.Globalization
char[] outputHeap = new char[actualLength];
fixed (char* pOutputHeap = &outputHeap[0])
{
- actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, pOutputHeap, actualLength);
+ actualLength = Interop.Globalization.ToAscii(flags, unicode, count, pOutputHeap, actualLength);
if (actualLength == 0 || actualLength > outputHeap.Length)
{
throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode));
@@ -77,7 +77,7 @@ namespace System.Globalization
{
Debug.Assert(!GlobalizationMode.Invariant);
- int realLen = Interop.GlobalizationInterop.ToUnicode(flags, ascii, count, output, outputLength);
+ int realLen = Interop.Globalization.ToUnicode(flags, ascii, count, output, outputLength);
if (realLen == 0)
{
@@ -108,8 +108,8 @@ namespace System.Globalization
get
{
int flags =
- (AllowUnassigned ? Interop.GlobalizationInterop.AllowUnassigned : 0) |
- (UseStd3AsciiRules ? Interop.GlobalizationInterop.UseStd3AsciiRules : 0);
+ (AllowUnassigned ? Interop.Globalization.AllowUnassigned : 0) |
+ (UseStd3AsciiRules ? Interop.Globalization.UseStd3AsciiRules : 0);
return (uint)flags;
}
}
@@ -123,7 +123,7 @@ namespace System.Globalization
/// </summary>
private static unsafe void CheckInvalidIdnCharacters(char* s, int count, uint flags, string paramName)
{
- if ((flags & Interop.GlobalizationInterop.UseStd3AsciiRules) == 0)
+ if ((flags & Interop.Globalization.UseStd3AsciiRules) == 0)
{
for (int i = 0; i < count; i++)
{
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/InternalGlobalizationHelper.cs b/src/System.Private.CoreLib/shared/System/Globalization/InternalGlobalizationHelper.cs
index f5eea1b62..60abcecf6 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/InternalGlobalizationHelper.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/InternalGlobalizationHelper.cs
@@ -6,7 +6,7 @@ namespace System.Globalization
{
internal class InternalGlobalizationHelper
{
- // Copied from the TimeSpan to be used inside the globalization code and avoid internal dependancy on TimeSpan class
+ // Copied from the TimeSpan to be used inside the globalization code and avoid internal dependency on TimeSpan class
internal static long TimeToTicks(int hour, int minute, int second)
{
// totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Unix.cs
index 51ff8095a..5e66c94b2 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Unix.cs
@@ -31,7 +31,7 @@ namespace System.Globalization
List<EraInfo> eras = new List<EraInfo>();
int lastMaxYear = GregorianCalendar.MaxYear;
- int latestEra = Interop.GlobalizationInterop.GetLatestJapaneseEra();
+ int latestEra = Interop.Globalization.GetLatestJapaneseEra();
for (int i = latestEra; i >= 0; i--)
{
DateTime dt;
@@ -79,7 +79,7 @@ namespace System.Globalization
int startYear;
int startMonth;
int startDay;
- bool result = Interop.GlobalizationInterop.GetJapaneseEraStartDate(
+ bool result = Interop.Globalization.GetJapaneseEraStartDate(
era,
out startYear,
out startMonth,
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs b/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs
index 9ea6c21c2..1d0180b00 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs
@@ -159,7 +159,7 @@ namespace System.Globalization
int month;
int day;
- ReadOnlySpan<char> valueSpan = value.AsReadOnlySpan();
+ ReadOnlySpan<char> valueSpan = value.AsSpan();
if (!Int32.TryParse(valueSpan.Slice(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) ||
!Int32.TryParse(valueSpan.Slice(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) ||
!Int32.TryParse(valueSpan.Slice(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day))
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs
index a25c1b938..443dbae53 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs
@@ -20,7 +20,7 @@ namespace System.Globalization
ValidateArguments(strInput, normalizationForm);
- int ret = Interop.GlobalizationInterop.IsNormalized(normalizationForm, strInput, strInput.Length);
+ int ret = Interop.Globalization.IsNormalized(normalizationForm, strInput, strInput.Length);
if (ret == -1)
{
@@ -45,7 +45,7 @@ namespace System.Globalization
for (int attempts = 2; attempts > 0; attempts--)
{
- int realLen = Interop.GlobalizationInterop.NormalizeString(normalizationForm, strInput, strInput.Length, buf, buf.Length);
+ int realLen = Interop.Globalization.NormalizeString(normalizationForm, strInput, strInput.Length, buf, buf.Length);
if (realLen == -1)
{
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
index dd2433d18..d13d3e8ce 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Security;
using System.Text;
@@ -64,6 +65,54 @@ namespace System.Globalization
return result;
}
+ internal unsafe void ChangeCase(ReadOnlySpan<char> source, Span<char> destination, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(destination.Length >= source.Length);
+
+ if (source.IsEmpty)
+ {
+ return;
+ }
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ {
+ fixed (char* pResult = &MemoryMarshal.GetReference(destination))
+ {
+ if (IsAsciiCasingSameAsInvariant)
+ {
+ int length = 0;
+ char* a = pSource, b = pResult;
+ if (toUpper)
+ {
+ while (length < source.Length && *a < 0x80)
+ {
+ *b++ = ToUpperAsciiInvariant(*a++);
+ length++;
+ }
+ }
+ else
+ {
+ while (length < source.Length && *a < 0x80)
+ {
+ *b++ = ToLowerAsciiInvariant(*a++);
+ length++;
+ }
+ }
+
+ if (length != source.Length)
+ {
+ ChangeCase(a, source.Length - length, b, destination.Length - length, toUpper);
+ }
+ }
+ else
+ {
+ ChangeCase(pSource, source.Length, pResult, destination.Length, toUpper);
+ }
+ }
+ }
+ }
+
private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!_invariantMode);
@@ -94,7 +143,7 @@ namespace System.Globalization
if (IsInvariant)
{
- Interop.GlobalizationInterop.ChangeCaseInvariant(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
+ Interop.Globalization.ChangeCaseInvariant(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
}
else
{
@@ -104,11 +153,11 @@ namespace System.Globalization
}
if (_needsTurkishCasing == Tristate.True)
{
- Interop.GlobalizationInterop.ChangeCaseTurkish(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
+ Interop.Globalization.ChangeCaseTurkish(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
}
else
{
- Interop.GlobalizationInterop.ChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
+ Interop.Globalization.ChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
index 8b27e42d1..015b37fcc 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
namespace System.Globalization
{
@@ -75,6 +76,43 @@ namespace System.Globalization
return result;
}
+ internal unsafe void ChangeCase(ReadOnlySpan<char> source, Span<char> destination, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(destination.Length >= source.Length);
+
+ if (source.IsEmpty)
+ {
+ return;
+ }
+
+ int ret;
+
+ // Check for Invariant to avoid A/V in LCMapStringEx
+ uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING;
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pResult = &MemoryMarshal.GetReference(destination))
+ {
+ ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName,
+ linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE),
+ pSource,
+ source.Length,
+ pResult,
+ source.Length,
+ null,
+ null,
+ _sortHandle);
+ }
+
+ if (ret == 0)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ }
+
+ Debug.Assert(ret == source.Length, "Expected getting the same length of the original span");
+ }
+
private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!_invariantMode);
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
index 59a3188bb..631c8c0f1 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
@@ -91,28 +91,6 @@ namespace System.Globalization
return Invariant.GetCaseInsensitiveHashCode(s);
}
- // Currently we don't have native functions to do this, so we do it the hard way
- internal static int IndexOfStringOrdinalIgnoreCase(string source, string value, int startIndex, int count)
- {
- if (count > source.Length || count < 0 || startIndex < 0 || startIndex > source.Length - count)
- {
- return -1;
- }
-
- return CultureInfo.InvariantCulture.CompareInfo.IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
- }
-
- // Currently we don't have native functions to do this, so we do it the hard way
- internal static int LastIndexOfStringOrdinalIgnoreCase(string source, string value, int startIndex, int count)
- {
- if (count > source.Length || count < 0 || startIndex < 0 || startIndex > source.Length - 1 || (startIndex - count + 1 < 0))
- {
- return -1;
- }
-
- return CultureInfo.InvariantCulture.CompareInfo.LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
- }
-
public virtual int ANSICodePage => _cultureData.IDEFAULTANSICODEPAGE;
public virtual int OEMCodePage => _cultureData.IDEFAULTOEMCODEPAGE;
@@ -280,6 +258,16 @@ namespace System.Globalization
}
}
+ internal void ToLowerAsciiInvariant(ReadOnlySpan<char> source, Span<char> destination)
+ {
+ Debug.Assert(destination.Length >= source.Length);
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = ToLowerAsciiInvariant(source[i]);
+ }
+ }
+
private unsafe string ToUpperAsciiInvariant(string s)
{
if (s.Length == 0)
@@ -326,6 +314,16 @@ namespace System.Globalization
}
}
+ internal void ToUpperAsciiInvariant(ReadOnlySpan<char> source, Span<char> destination)
+ {
+ Debug.Assert(destination.Length >= source.Length);
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = ToUpperAsciiInvariant(source[i]);
+ }
+ }
+
private static char ToLowerAsciiInvariant(char c)
{
if ((uint)(c - 'A') <= (uint)('Z' - 'A'))
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs b/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
index bf12b246b..a66e4600a 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
@@ -4,6 +4,7 @@
using System.Text;
using System.Diagnostics;
+using System.Runtime.InteropServices;
namespace System.Globalization
{
@@ -315,7 +316,7 @@ namespace System.Globalization
if (nextChar >= 0 && nextChar != (int)'%')
{
char nextCharChar = (char)nextChar;
- StringBuilder origStringBuilder = FormatCustomized(value, ReadOnlySpan<char>.DangerousCreate(null, ref nextCharChar, 1), dtfi, result);
+ StringBuilder origStringBuilder = FormatCustomized(value, MemoryMarshal.CreateReadOnlySpan<char>(ref nextCharChar, 1), dtfi, result);
Debug.Assert(ReferenceEquals(origStringBuilder, result));
tokenLen = 2;
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
index 34164abc3..d9fcf6571 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
@@ -240,7 +240,7 @@ namespace System.IO
{
FlushWriteBuffer();
}
- catch (IOException) when (!disposing)
+ catch (Exception e) when (IsIoRelatedException(e) && !disposing)
{
// On finalization, ignore failures from trying to flush the write buffer,
// e.g. if this stream is wrapping a pipe and the pipe is now broken.
@@ -635,12 +635,12 @@ namespace System.IO
/// <param name="source">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
- private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
+ private ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
Debug.Assert(_useAsyncIO);
if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
+ return new ValueTask(Task.FromCanceled(cancellationToken));
if (_fileHandle.IsClosed)
throw Error.GetFileNotOpen();
@@ -667,11 +667,11 @@ namespace System.IO
source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length));
_writePos += source.Length;
- return Task.CompletedTask;
+ return default;
}
catch (Exception exc)
{
- return Task.FromException(exc);
+ return new ValueTask(Task.FromException(exc));
}
finally
{
@@ -682,7 +682,7 @@ namespace System.IO
// Otherwise, issue the whole request asynchronously.
_asyncState.ReadOnlyMemory = source;
- return waitTask.ContinueWith((t, s) =>
+ return new ValueTask(waitTask.ContinueWith((t, s) =>
{
// The options available on Unix for writing asynchronously to an arbitrary file
// handle typically amount to just using another thread to do the synchronous write,
@@ -702,7 +702,7 @@ namespace System.IO
thisRef.WriteSpan(readOnlyMemory.Span);
}
finally { thisRef._asyncState.Release(); }
- }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
+ }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default));
}
/// <summary>Sets the current position of this stream to the given value.</summary>
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
index 477b9430f..291a30bb5 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
@@ -268,7 +268,15 @@ namespace System.IO
// want us to do this.
if (_writePos > 0)
{
- FlushWriteBuffer(!disposing);
+ try
+ {
+ FlushWriteBuffer(!disposing);
+ }
+ catch (Exception e) when (IsIoRelatedException(e) && !disposing)
+ {
+ // On finalization, ignore failures from trying to flush the write buffer,
+ // e.g. if this stream is wrapping a pipe and the pipe is now broken.
+ }
}
}
}
@@ -848,9 +856,7 @@ namespace System.IO
Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!");
// Create and store async stream class library specific data in the async result
- FileStreamCompletionSource completionSource = destination.TryGetArray(out ArraySegment<byte> memoryArray) ?
- new FileStreamCompletionSource(this, numBufferedBytesRead, memoryArray.Array) :
- new MemoryFileStreamCompletionSource(this, numBufferedBytesRead, destination);
+ FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, numBufferedBytesRead, destination);
NativeOverlapped* intOverlapped = completionSource.Overlapped;
// Calculate position in the file we should be at after the read is done
@@ -955,7 +961,7 @@ namespace System.IO
return completionSource.Task;
}
- private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
+ private ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
Debug.Assert(_useAsyncIO);
Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
@@ -999,7 +1005,7 @@ namespace System.IO
// completely, we want to do the asynchronous flush/write as part of this operation
// rather than waiting until the next write that fills the buffer.
if (source.Length != remainingBuffer)
- return Task.CompletedTask;
+ return default;
Debug.Assert(_writePos == _bufferLength);
}
@@ -1045,7 +1051,7 @@ namespace System.IO
flushTask.IsFaulted ||
flushTask.IsCanceled)
{
- return flushTask;
+ return new ValueTask(flushTask);
}
}
@@ -1055,10 +1061,10 @@ namespace System.IO
// Finally, issue the write asynchronously, and return a Task that logically
// represents the write operation, including any flushing done.
Task writeTask = WriteAsyncInternalCore(source, cancellationToken);
- return
+ return new ValueTask(
(flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask :
(writeTask.Status == TaskStatus.RanToCompletion) ? flushTask :
- Task.WhenAll(flushTask, writeTask);
+ Task.WhenAll(flushTask, writeTask));
}
private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
@@ -1069,9 +1075,7 @@ namespace System.IO
Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!");
// Create and store async stream class library specific data in the async result
- FileStreamCompletionSource completionSource = MemoryMarshal.TryGetArray(source, out ArraySegment<byte> array) ?
- new FileStreamCompletionSource(this, 0, array.Array) :
- new MemoryFileStreamCompletionSource(this, 0, source);
+ FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, 0, source);
NativeOverlapped* intOverlapped = completionSource.Overlapped;
if (CanSeek)
@@ -1315,7 +1319,7 @@ namespace System.IO
int bufferedBytes = _readLength - _readPos;
if (bufferedBytes > 0)
{
- await destination.WriteAsync(GetBuffer(), _readPos, bufferedBytes, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(new ReadOnlyMemory<byte>(GetBuffer(), _readPos, bufferedBytes), cancellationToken).ConfigureAwait(false);
_readPos = _readLength = 0;
}
}
@@ -1341,7 +1345,6 @@ namespace System.IO
// Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
// we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool.
byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
- bufferSize = 0; // repurpose bufferSize to be the high water mark for the buffer, to avoid an extra field in the state machine
// Allocate an Overlapped we can use repeatedly for all operations
var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
@@ -1448,13 +1451,6 @@ namespace System.IO
{
readAwaitable._position += numBytesRead;
}
-
- // (and keep track of the maximum number of bytes in the buffer we used, to avoid excessive and unnecessary
- // clearing of the buffer before we return it to the pool)
- if (numBytesRead > bufferSize)
- {
- bufferSize = numBytesRead;
- }
}
finally
{
@@ -1475,7 +1471,7 @@ namespace System.IO
}
// Write out the read data.
- await destination.WriteAsync(copyBuffer, 0, (int)readAwaitable._numBytes, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(new ReadOnlyMemory<byte>(copyBuffer, 0, (int)readAwaitable._numBytes), cancellationToken).ConfigureAwait(false);
}
}
finally
@@ -1484,8 +1480,7 @@ namespace System.IO
cancellationReg.Dispose();
awaitableOverlapped.Dispose();
- Array.Clear(copyBuffer, 0, bufferSize);
- ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: false);
+ ArrayPool<byte>.Shared.Return(copyBuffer);
// Make sure the stream's current position reflects where we ended up
if (!_fileHandle.IsClosed && CanSeek)
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.cs
index 65c63bcc5..717b73ff1 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.cs
@@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
+using System.Security;
namespace System.IO
{
@@ -457,10 +458,10 @@ namespace System.IO
if (IsClosed)
throw Error.GetFileNotOpen();
- return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
+ return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
}
- public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
{
if (!_useAsyncIO || GetType() != typeof(FileStream))
{
@@ -472,7 +473,7 @@ namespace System.IO
if (cancellationToken.IsCancellationRequested)
{
- return Task.FromCanceled<int>(cancellationToken);
+ return new ValueTask(Task.FromCanceled<int>(cancellationToken));
}
if (IsClosed)
@@ -673,6 +674,22 @@ namespace System.IO
internal virtual bool IsClosed => _fileHandle.IsClosed;
+ private static bool IsIoRelatedException(Exception e) =>
+ // These all derive from IOException
+ // DirectoryNotFoundException
+ // DriveNotFoundException
+ // EndOfStreamException
+ // FileLoadException
+ // FileNotFoundException
+ // PathTooLongException
+ // PipeException
+ e is IOException ||
+ // Note that SecurityException is only thrown on runtimes that support CAS
+ // e is SecurityException ||
+ e is UnauthorizedAccessException ||
+ e is NotSupportedException ||
+ (e is ArgumentException && !(e is ArgumentNullException));
+
/// <summary>
/// Gets the array used for buffering reading and writing.
/// If the array hasn't been allocated, this will lazily allocate it.
@@ -836,7 +853,7 @@ namespace System.IO
if (!IsAsync)
return base.BeginWrite(array, offset, numBytes, callback, state);
else
- return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None), callback, state);
+ return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None).AsTask(), callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
index 4e19f465b..82274b131 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
@@ -36,7 +36,7 @@ namespace System.IO
private long _result; // Using long since this needs to be used in Interlocked APIs
// Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations)
- internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes)
+ protected FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes)
: base(TaskCreationOptions.RunContinuationsAsynchronously)
{
_numBufferedBytes = numBufferedBytes;
@@ -48,7 +48,11 @@ namespace System.IO
// thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path
// for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's
// buffer is used) and where operations on the FileStream are not being performed concurrently.
- _overlapped = (bytes == null || ReferenceEquals(bytes, _stream._buffer)) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
+ Debug.Assert((bytes == null || ReferenceEquals(bytes, _stream._buffer)));
+
+ // The _preallocatedOverlapped is null if the internal buffer was never created, so we check for
+ // a non-null bytes before using the stream's _preallocatedOverlapped
+ _overlapped = bytes != null && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
_stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) :
_stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes);
Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null");
@@ -217,6 +221,17 @@ namespace System.IO
}
}
}
+
+ public static FileStreamCompletionSource Create(FileStream stream, int numBufferedBytesRead, ReadOnlyMemory<byte> memory)
+ {
+ // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource,
+ // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived
+ // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case
+ // where the underlying memory is backed by pre-pinned buffers.
+ return MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> buffer) && ReferenceEquals(buffer.Array, stream._buffer) ?
+ new FileStreamCompletionSource(stream, numBufferedBytesRead, buffer.Array) :
+ new MemoryFileStreamCompletionSource(stream, numBufferedBytesRead, memory);
+ }
}
/// <summary>
@@ -231,7 +246,6 @@ namespace System.IO
internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) :
base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes
{
- Debug.Assert(!MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> array), "The base should be used directly if we can get the array.");
_handle = memory.Retain(pin: true);
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs b/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
index c5e5ea918..ffe7f6093 100644
--- a/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
@@ -448,7 +448,7 @@ namespace System.IO
// something other than an array and this is a MemoryStream-derived type that doesn't override Read(Span<byte>) will
// it then fall back to doing the ArrayPool/copy behavior.
return new ValueTask<int>(
- destination.TryGetArray(out ArraySegment<byte> destinationArray) ?
+ MemoryMarshal.TryGetArray(destination, out ArraySegment<byte> destinationArray) ?
Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) :
Read(destination.Span));
}
@@ -752,11 +752,11 @@ namespace System.IO
}
}
- public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
- return Task.FromCanceled(cancellationToken);
+ return new ValueTask(Task.FromCanceled(cancellationToken));
}
try
@@ -771,15 +771,15 @@ namespace System.IO
{
Write(source.Span);
}
- return Task.CompletedTask;
+ return default;
}
catch (OperationCanceledException oce)
{
- return Task.FromCancellation<VoidTaskResult>(oce);
+ return new ValueTask(Task.FromCancellation<VoidTaskResult>(oce));
}
catch (Exception exception)
{
- return Task.FromException(exception);
+ return new ValueTask(Task.FromException(exception));
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs b/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs
index 1143c0520..fd24cc810 100644
--- a/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs
@@ -44,91 +44,24 @@ namespace System.IO
return result;
}
- /// <summary>
- /// Try to remove relative segments from the given path (without combining with a root).
- /// </summary>
- /// <param name="skip">Skip the specified number of characters before evaluating.</param>
- private static string RemoveRelativeSegments(string path, int skip = 0)
+ public static string GetFullPath(string path, string basePath)
{
- bool flippedSeparator = false;
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
- // Remove "//", "/./", and "/../" from the path by copying each character to the output,
- // except the ones we're removing, such that the builder contains the normalized path
- // at the end.
- var sb = StringBuilderCache.Acquire(path.Length);
- if (skip > 0)
- {
- sb.Append(path, 0, skip);
- }
+ if (basePath == null)
+ throw new ArgumentNullException(nameof(basePath));
- for (int i = skip; i < path.Length; i++)
- {
- char c = path[i];
-
- if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length)
- {
- // Skip this character if it's a directory separator and if the next character is, too,
- // e.g. "parent//child" => "parent/child"
- if (PathInternal.IsDirectorySeparator(path[i + 1]))
- {
- continue;
- }
-
- // Skip this character and the next if it's referring to the current directory,
- // e.g. "parent/./child" =? "parent/child"
- if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) &&
- path[i + 1] == '.')
- {
- i++;
- continue;
- }
-
- // Skip this character and the next two if it's referring to the parent directory,
- // e.g. "parent/child/../grandchild" => "parent/grandchild"
- if (i + 2 < path.Length &&
- (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) &&
- path[i + 1] == '.' && path[i + 2] == '.')
- {
- // Unwind back to the last slash (and if there isn't one, clear out everything).
- int s;
- for (s = sb.Length - 1; s >= 0; s--)
- {
- if (PathInternal.IsDirectorySeparator(sb[s]))
- {
- sb.Length = s;
- break;
- }
- }
- if (s < 0)
- {
- sb.Length = 0;
- }
-
- i += 2;
- continue;
- }
- }
-
- // Normalize the directory separator if needed
- if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar)
- {
- c = PathInternal.DirectorySeparatorChar;
- flippedSeparator = true;
- }
-
- sb.Append(c);
- }
+ if (!IsPathFullyQualified(basePath))
+ throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
- if (flippedSeparator || sb.Length != path.Length)
- {
- return StringBuilderCache.GetStringAndRelease(sb);
- }
- else
- {
- // We haven't changed the source path, return the original
- StringBuilderCache.Release(sb);
- return path;
- }
+ if (basePath.Contains('\0') || path.Contains('\0'))
+ throw new ArgumentException(SR.Argument_InvalidPathChars);
+
+ if (IsPathFullyQualified(path))
+ return GetFullPath(path);
+
+ return GetFullPath(CombineInternal(basePath, path));
}
private static string RemoveLongPathPrefix(string path)
@@ -175,18 +108,27 @@ namespace System.IO
if (path == null)
return false;
+ return IsPathRooted(path.AsSpan());
+ }
+
+ public static bool IsPathRooted(ReadOnlySpan<char> path)
+ {
return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar;
}
- // The resulting string is null if path is null. If the path is empty or
- // only contains whitespace characters an ArgumentException gets thrown.
+ /// <summary>
+ /// Returns the path root or null if path is empty or null.
+ /// </summary>
public static string GetPathRoot(string path)
{
- if (path == null) return null;
- if (PathInternal.IsEffectivelyEmpty(path))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
+ if (PathInternal.IsEffectivelyEmpty(path)) return null;
- return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty;
+ return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : string.Empty;
+ }
+
+ public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
+ {
+ return PathInternal.IsEffectivelyEmpty(path) && IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan<char>.Empty;
}
/// <summary>Gets whether the system is case-sensitive.</summary>
diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs
index 5d92d3b49..b921db9e6 100644
--- a/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs
@@ -27,74 +27,105 @@ namespace System.IO
(char)31
};
- // The max total path is 260, and the max individual component length is 255.
- // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars.
- internal const int MaxPath = 260;
-
- // Expands the given path to a fully qualified path.
+ // Expands the given path to a fully qualified path.
public static string GetFullPath(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
- // Embedded null characters are the only invalid character case we want to check up front.
+ // If the path would normalize to string empty, we'll consider it empty
+ if (PathInternal.IsEffectivelyEmpty(path))
+ throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
+
+ // Embedded null characters are the only invalid character case we trully care about.
// This is because the nulls will signal the end of the string to Win32 and therefore have
- // unpredictable results. Other invalid characters we give a chance to be normalized out.
+ // unpredictable results.
if (path.IndexOf('\0') != -1)
throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
if (PathInternal.IsExtended(path))
{
- // We can't really know what is valid for all cases of extended paths.
- //
- // - object names can include other characters as well (':', '/', etc.)
- // - even file objects have different rules (pipe names can contain most characters)
- //
- // As such we will do no further analysis of extended paths to avoid blocking known and unknown
- // scenarios as well as minimizing compat breaks should we block now and need to unblock later.
+ // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\
+ // paths and neither should we. Even if we wanted to GetFullPathName does not work
+ // properly with device paths. If one wants to pass a \\?\ path through normalization
+ // one can chop off the prefix, pass it to GetFullPath and add it again.
return path;
}
- bool isDevice = PathInternal.IsDevice(path);
- if (!isDevice)
- {
- // Toss out paths with colons that aren't a valid drive specifier.
- // Cannot start with a colon and can only be of the form "C:".
- // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.)
- int startIndex = PathInternal.PathStartSkip(path);
+ return PathHelper.Normalize(path);
+ }
- // Move past the colon
- startIndex += 2;
+ public static string GetFullPath(string path, string basePath)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
- if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar)
- || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2]))
- || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1))
- {
- throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path));
- }
- }
+ if (basePath == null)
+ throw new ArgumentNullException(nameof(basePath));
- // Technically this doesn't matter but we used to throw for this case
- if (PathInternal.IsEffectivelyEmpty(path))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
+ if (!IsPathFullyQualified(basePath))
+ throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
+
+ if (basePath.Contains('\0') || path.Contains('\0'))
+ throw new ArgumentException(SR.Argument_InvalidPathChars);
+
+ if (IsPathFullyQualified(path))
+ return GetFullPath(path);
- // We don't want to check invalid characters for device format- see comments for extended above
- string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true);
+ int length = path.Length;
+ string combinedPath = null;
- if (!isDevice)
+ if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])))
+ {
+ // Path is current drive rooted i.e. starts with \:
+ // "\Foo" and "C:\Bar" => "C:\Foo"
+ // "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo"
+ combinedPath = Join(GetPathRoot(basePath.AsSpan()), path);
+ }
+ else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)
+ {
+ // Drive relative paths
+ Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2]));
+
+ if (StringSpanHelpers.Equals(GetVolumeName(path), GetVolumeName(basePath)))
+ {
+ // Matching root
+ // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
+ // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
+ combinedPath = Join(basePath, path.AsSpan().Slice(2));
+ }
+ else
+ {
+ // No matching root, root to specified drive
+ // "D:Foo" and "C:\Bar" => "D:Foo"
+ // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo"
+ combinedPath = !PathInternal.IsDevice(basePath)
+ ? path.Insert(2, @"\")
+ : length == 2
+ ? JoinInternal(basePath.AsSpan().Slice(0, 4), path, @"\")
+ : JoinInternal(basePath.AsSpan().Slice(0, 4), path.AsSpan().Slice(0, 2), @"\", path.AsSpan().Slice(2));
+ }
+ }
+ else
{
- // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked)
- if (PathInternal.HasWildCardCharacters(fullPath))
- throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
+ // "Simple" relative path
+ // "Foo" and "C:\Bar" => "C:\Bar\Foo"
+ // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
+ combinedPath = JoinInternal(basePath, path);
}
- return fullPath;
+ // Device paths are normalized by definition, so passing something of this format
+ // to GetFullPath() won't do anything by design. Additionally, GetFullPathName() in
+ // Windows doesn't root them properly. As such we need to manually remove segments.
+ return PathInternal.IsDevice(combinedPath)
+ ? RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath))
+ : GetFullPath(combinedPath);
}
public static string GetTempPath()
{
- StringBuilder sb = StringBuilderCache.Acquire(MaxPath);
- uint r = Interop.Kernel32.GetTempPathW(MaxPath, sb);
+ StringBuilder sb = StringBuilderCache.Acquire(Interop.Kernel32.MAX_PATH);
+ uint r = Interop.Kernel32.GetTempPathW(Interop.Kernel32.MAX_PATH, sb);
if (r == 0)
throw Win32Marshal.GetExceptionForLastWin32Error();
return GetFullPath(StringBuilderCache.GetStringAndRelease(sb));
@@ -106,7 +137,7 @@ namespace System.IO
{
string path = GetTempPath();
- StringBuilder sb = StringBuilderCache.Acquire(MaxPath);
+ StringBuilder sb = StringBuilderCache.Acquire(Interop.Kernel32.MAX_PATH);
uint r = Interop.Kernel32.GetTempFileNameW(path, "tmp", 0, sb);
if (r == 0)
throw Win32Marshal.GetExceptionForLastWin32Error();
@@ -117,14 +148,14 @@ namespace System.IO
// if it starts with a backslash ("\") or a valid drive letter and a colon (":").
public static bool IsPathRooted(string path)
{
- if (path != null)
- {
- int length = path.Length;
- if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) ||
- (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar))
- return true;
- }
- return false;
+ return path != null && IsPathRooted(path.AsSpan());
+ }
+
+ public static bool IsPathRooted(ReadOnlySpan<char> path)
+ {
+ int length = path.Length;
+ return (length >= 1 && PathInternal.IsDirectorySeparator(path[0]))
+ || (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar);
}
// Returns the root portion of the given path. The resulting string
@@ -138,18 +169,82 @@ namespace System.IO
// only contains whitespace characters an ArgumentException gets thrown.
public static string GetPathRoot(string path)
{
- if (path == null) return null;
if (PathInternal.IsEffectivelyEmpty(path))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
+ return null;
- // Need to return the normalized directory separator
- path = PathInternal.NormalizeDirectorySeparators(path);
+ ReadOnlySpan<char> result = GetPathRoot(path.AsSpan());
+ if (path.Length == result.Length)
+ return PathInternal.NormalizeDirectorySeparators(path);
+
+ return PathInternal.NormalizeDirectorySeparators(new string(result));
+ }
+
+ /// <remarks>
+ /// Unlike the string overload, this method will not normalize directory separators.
+ /// </remarks>
+ public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
+ {
+ if (PathInternal.IsEffectivelyEmpty(path))
+ return ReadOnlySpan<char>.Empty;
int pathRoot = PathInternal.GetRootLength(path);
- return pathRoot <= 0 ? string.Empty : path.Substring(0, pathRoot);
+ return pathRoot <= 0 ? ReadOnlySpan<char>.Empty : path.Slice(0, pathRoot);
}
/// <summary>Gets whether the system is case-sensitive.</summary>
internal static bool IsCaseSensitive { get { return false; } }
+
+ /// <summary>
+ /// Returns the volume name for dos, UNC and device paths.
+ /// </summary>
+ internal static ReadOnlySpan<char> GetVolumeName(ReadOnlySpan<char> path)
+ {
+ // 3 cases: UNC ("\\server\share"), Device ("\\?\C:\"), or Dos ("C:\")
+ ReadOnlySpan<char> root = GetPathRoot(path);
+ if (root.Length == 0)
+ return root;
+
+ int offset = GetUncRootLength(path);
+ if (offset >= 0)
+ {
+ // Cut from "\\?\UNC\Server\Share" to "Server\Share"
+ // Cut from "\\Server\Share" to "Server\Share"
+ return TrimEndingDirectorySeparator(root.Slice(offset));
+ }
+ else if (PathInternal.IsDevice(path))
+ {
+ return TrimEndingDirectorySeparator(root.Slice(4)); // Cut from "\\?\C:\" to "C:"
+ }
+
+ return TrimEndingDirectorySeparator(root); // e.g. "C:"
+ }
+
+ /// <summary>
+ /// Trims the ending directory separator if present.
+ /// </summary>
+ /// <param name="path"></param>
+ internal static ReadOnlySpan<char> TrimEndingDirectorySeparator(ReadOnlySpan<char> path) =>
+ PathInternal.EndsInDirectorySeparator(path) ?
+ path.Slice(0, path.Length - 1) :
+ path;
+
+ /// <summary>
+ /// Returns offset as -1 if the path is not in Unc format, otherwise returns the root length.
+ /// </summary>
+ /// <param name="path"></param>
+ /// <returns></returns>
+ internal static int GetUncRootLength(ReadOnlySpan<char> path)
+ {
+ bool isDevice = PathInternal.IsDevice(path);
+
+ if (!isDevice && StringSpanHelpers.Equals(path.Slice(0, 2), @"\\") )
+ return 2;
+ else if (isDevice && path.Length >= 8
+ && (StringSpanHelpers.Equals(path.Slice(0, 8), PathInternal.UncExtendedPathPrefix)
+ || StringSpanHelpers.Equals(path.Slice(5, 4), @"UNC\")))
+ return 8;
+
+ return -1;
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.cs b/src/System.Private.CoreLib/shared/System/IO/Path.cs
index 9f3f48600..41ae1cd0b 100644
--- a/src/System.Private.CoreLib/shared/System/IO/Path.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/Path.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Text;
namespace System.IO
@@ -50,7 +51,7 @@ namespace System.IO
s = path.Substring(0, i);
break;
}
- if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break;
+ if (PathInternal.IsDirectorySeparator(ch)) break;
}
if (extension != null && path.Length != 0)
@@ -65,70 +66,133 @@ namespace System.IO
return null;
}
- // Returns the directory path of a file path. This method effectively
- // removes the last element of the given file path, i.e. it returns a
- // string consisting of all characters up to but not including the last
- // backslash ("\") in the file path. The returned value is null if the file
- // path is null or if the file path denotes a root (such as "\", "C:", or
- // "\\server\share").
+ /// <summary>
+ /// Returns the directory portion of a file path. This method effectively
+ /// removes the last segment of the given file path, i.e. it returns a
+ /// string consisting of all characters up to but not including the last
+ /// backslash ("\") in the file path. The returned value is null if the
+ /// specified path is null, empty, or a root (such as "\", "C:", or
+ /// "\\server\share").
+ /// </summary>
+ /// <remarks>
+ /// Directory separators are normalized in the returned string.
+ /// </remarks>
public static string GetDirectoryName(string path)
{
- if (path == null)
+ if (path == null || PathInternal.IsEffectivelyEmpty(path))
return null;
+ int end = GetDirectoryNameOffset(path);
+ return end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null;
+ }
+
+ /// <summary>
+ /// Returns the directory portion of a file path. The returned value is empty
+ /// if the specified path is null, empty, or a root (such as "\", "C:", or
+ /// "\\server\share").
+ /// </summary>
+ /// <remarks>
+ /// Unlike the string overload, this method will not normalize directory separators.
+ /// </remarks>
+ public static ReadOnlySpan<char> GetDirectoryName(ReadOnlySpan<char> path)
+ {
if (PathInternal.IsEffectivelyEmpty(path))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
+ return ReadOnlySpan<char>.Empty;
- path = PathInternal.NormalizeDirectorySeparators(path);
- int root = PathInternal.GetRootLength(path);
+ int end = GetDirectoryNameOffset(path);
+ return end >= 0 ? path.Slice(0, end) : ReadOnlySpan<char>.Empty;
+ }
- int i = path.Length;
- if (i > root)
- {
- while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ;
- return path.Substring(0, i);
- }
-
- return null;
+ private static int GetDirectoryNameOffset(ReadOnlySpan<char> path)
+ {
+ int rootLength = PathInternal.GetRootLength(path);
+ int end = path.Length;
+ if (end <= rootLength)
+ return -1;
+
+ while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end]));
+
+ // Trim off any remaining separators (to deal with C:\foo\\bar)
+ while (end > rootLength && PathInternal.IsDirectorySeparator(path[end - 1]))
+ end--;
+
+ return end;
}
- // Returns the extension of the given path. The returned value includes the
- // period (".") character of the extension except when you have a terminal period when you get string.Empty, such as ".exe" or
- // ".cpp". The returned value is null if the given path is
- // null or if the given path does not include an extension.
+ /// <summary>
+ /// Returns the extension of the given path. The returned value includes the period (".") character of the
+ /// extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp".
+ /// The returned value is null if the given path is null or empty if the given path does not include an
+ /// extension.
+ /// </summary>
public static string GetExtension(string path)
{
if (path == null)
return null;
+ return new string(GetExtension(path.AsSpan()));
+ }
+
+ /// <summary>
+ /// Returns the extension of the given path.
+ /// </summary>
+ /// <remarks>
+ /// The returned value is an empty ReadOnlySpan if the given path does not include an extension.
+ /// </remarks>
+ public static ReadOnlySpan<char> GetExtension(ReadOnlySpan<char> path)
+ {
int length = path.Length;
+
for (int i = length - 1; i >= 0; i--)
{
char ch = path[i];
if (ch == '.')
{
if (i != length - 1)
- return path.Substring(i, length - i);
+ return path.Slice(i, length - i);
else
- return string.Empty;
+ return ReadOnlySpan<char>.Empty;
}
- if (PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ if (PathInternal.IsDirectorySeparator(ch))
break;
}
- return string.Empty;
+ return ReadOnlySpan<char>.Empty;
}
- // Returns the name and extension parts of the given path. The resulting
- // string contains the characters of path that follow the last
- // separator in path. The resulting string is null if path is null.
+ /// <summary>
+ /// Returns the name and extension parts of the given path. The resulting string contains
+ /// the characters of path that follow the last separator in path. The resulting string is
+ /// null if path is null.
+ /// </summary>
public static string GetFileName(string path)
{
if (path == null)
return null;
- int offset = PathInternal.FindFileNameIndex(path);
- int count = path.Length - offset;
- return path.Substring(offset, count);
+ ReadOnlySpan<char> result = GetFileName(path.AsSpan());
+ if (path.Length == result.Length)
+ return path;
+
+ return new string(result);
+ }
+
+ /// <summary>
+ /// The returned ReadOnlySpan contains the characters of the path that follows the last separator in path.
+ /// </summary>
+ public static ReadOnlySpan<char> GetFileName(ReadOnlySpan<char> path)
+ {
+ int root = GetPathRoot(path).Length;
+
+ // We don't want to cut off "C:\file.txt:stream" (i.e. should be "file.txt:stream")
+ // but we *do* want "C:Foo" => "Foo". This necessitates checking for the root.
+
+ for (int i = path.Length; --i >= 0;)
+ {
+ if (i < root || PathInternal.IsDirectorySeparator(path[i]))
+ return path.Slice(i + 1, path.Length - i - 1);
+ }
+
+ return path;
}
public static string GetFileNameWithoutExtension(string path)
@@ -136,17 +200,29 @@ namespace System.IO
if (path == null)
return null;
- int length = path.Length;
- int offset = PathInternal.FindFileNameIndex(path);
+ ReadOnlySpan<char> result = GetFileNameWithoutExtension(path.AsSpan());
+ if (path.Length == result.Length)
+ return path;
+
+ return new string(result);
+ }
- int end = path.LastIndexOf('.', length - 1, length - offset);
- return end == -1 ?
- path.Substring(offset) : // No extension was found
- path.Substring(offset, end - offset);
+ /// <summary>
+ /// Returns the characters between the last separator and last (.) in the path.
+ /// </summary>
+ public static ReadOnlySpan<char> GetFileNameWithoutExtension(ReadOnlySpan<char> path)
+ {
+ ReadOnlySpan<char> fileName = GetFileName(path);
+ int lastPeriod = fileName.LastIndexOf('.');
+ return lastPeriod == -1 ?
+ fileName : // No extension was found
+ fileName.Slice(0, lastPeriod);
}
- // Returns a cryptographically strong random 8.3 string that can be
- // used as either a folder name or a file name.
+ /// <summary>
+ /// Returns a cryptographically strong random 8.3 string that can be
+ /// used as either a folder name or a file name.
+ /// </summary>
public static unsafe string GetRandomFileName()
{
byte* pKey = stackalloc byte[KeyLength];
@@ -176,29 +252,40 @@ namespace System.IO
public static bool IsPathFullyQualified(string path)
{
if (path == null)
- {
throw new ArgumentNullException(nameof(path));
- }
+
+ return IsPathFullyQualified(path.AsSpan());
+ }
+
+ public static bool IsPathFullyQualified(ReadOnlySpan<char> path)
+ {
return !PathInternal.IsPartiallyQualified(path);
}
- // Tests if a path includes a file extension. The result is
- // true if the characters that follow the last directory
- // separator ('\\' or '/') or volume separator (':') in the path include
- // a period (".") other than a terminal period. The result is false otherwise.
+ /// <summary>
+ /// Tests if a path's file name includes a file extension. A trailing period
+ /// is not considered an extension.
+ /// </summary>
public static bool HasExtension(string path)
{
if (path != null)
{
- for (int i = path.Length - 1; i >= 0; i--)
+ return HasExtension(path.AsSpan());
+ }
+ return false;
+ }
+
+ public static bool HasExtension(ReadOnlySpan<char> path)
+ {
+ for (int i = path.Length - 1; i >= 0; i--)
+ {
+ char ch = path[i];
+ if (ch == '.')
{
- char ch = path[i];
- if (ch == '.')
- {
- return i != path.Length - 1;
- }
- if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break;
+ return i != path.Length - 1;
}
+ if (PathInternal.IsDirectorySeparator(ch))
+ break;
}
return false;
}
@@ -208,7 +295,7 @@ namespace System.IO
if (path1 == null || path2 == null)
throw new ArgumentNullException((path1 == null) ? nameof(path1) : nameof(path2));
- return CombineNoChecks(path1, path2);
+ return CombineInternal(path1, path2);
}
public static string Combine(string path1, string path2, string path3)
@@ -216,7 +303,7 @@ namespace System.IO
if (path1 == null || path2 == null || path3 == null)
throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : nameof(path3));
- return CombineNoChecks(path1, path2, path3);
+ return CombineInternal(path1, path2, path3);
}
public static string Combine(string path1, string path2, string path3, string path4)
@@ -224,7 +311,7 @@ namespace System.IO
if (path1 == null || path2 == null || path3 == null || path4 == null)
throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : (path3 == null) ? nameof(path3) : nameof(path4));
- return CombineNoChecks(path1, path2, path3, path4);
+ return CombineInternal(path1, path2, path3, path4);
}
public static string Combine(params string[] paths)
@@ -263,7 +350,7 @@ namespace System.IO
}
char ch = paths[i][paths[i].Length - 1];
- if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ if (!PathInternal.IsDirectorySeparator(ch))
finalSize++;
}
@@ -283,7 +370,7 @@ namespace System.IO
else
{
char ch = finalPath[finalPath.Length - 1];
- if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ if (!PathInternal.IsDirectorySeparator(ch))
{
finalPath.Append(PathInternal.DirectorySeparatorChar);
}
@@ -295,120 +382,250 @@ namespace System.IO
return StringBuilderCache.GetStringAndRelease(finalPath);
}
- private static string CombineNoChecks(string path1, string path2)
- {
- if (path2.Length == 0)
- return path1;
+ // Unlike Combine(), Join() methods do not consider rooting. They simply combine paths, ensuring that there
+ // is a directory separator between them.
+ public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2)
+ {
if (path1.Length == 0)
- return path2;
-
- if (IsPathRooted(path2))
- return path2;
+ return new string(path2);
+ if (path2.Length == 0)
+ return new string(path1);
- char ch = path1[path1.Length - 1];
- return PathInternal.IsDirectoryOrVolumeSeparator(ch) ?
- path1 + path2 :
- path1 + PathInternal.DirectorySeparatorCharAsString + path2;
+ return JoinInternal(path1, path2);
}
- private static string CombineNoChecks(string path1, string path2, string path3)
+ public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3)
{
if (path1.Length == 0)
- return CombineNoChecks(path2, path3);
+ return Join(path2, path3);
+
if (path2.Length == 0)
- return CombineNoChecks(path1, path3);
+ return Join(path1, path3);
+
if (path3.Length == 0)
- return CombineNoChecks(path1, path2);
+ return Join(path1, path2);
- if (IsPathRooted(path3))
- return path3;
- if (IsPathRooted(path2))
- return CombineNoChecks(path2, path3);
+ return JoinInternal(path1, path2, path3);
+ }
- bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]);
- bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]);
+ public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, Span<char> destination, out int charsWritten)
+ {
+ charsWritten = 0;
+ if (path1.Length == 0 && path2.Length == 0)
+ return true;
- if (hasSep1 && hasSep2)
- {
- return path1 + path2 + path3;
- }
- else if (hasSep1)
- {
- return path1 + path2 + PathInternal.DirectorySeparatorCharAsString + path3;
- }
- else if (hasSep2)
+ if (path1.Length == 0 || path2.Length == 0)
{
- return path1 + PathInternal.DirectorySeparatorCharAsString + path2 + path3;
- }
- else
- {
- // string.Concat only has string-based overloads up to four arguments; after that requires allocating
- // a params string[]. Instead, try to use a cached StringBuilder.
- StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + 2);
- sb.Append(path1)
- .Append(PathInternal.DirectorySeparatorChar)
- .Append(path2)
- .Append(PathInternal.DirectorySeparatorChar)
- .Append(path3);
- return StringBuilderCache.GetStringAndRelease(sb);
+ ref ReadOnlySpan<char> pathToUse = ref path1.Length == 0 ? ref path2 : ref path1;
+ if (destination.Length < pathToUse.Length)
+ {
+ return false;
+ }
+
+ pathToUse.CopyTo(destination);
+ charsWritten = pathToUse.Length;
+ return true;
}
+
+ bool needsSeparator = !(PathInternal.EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2));
+ int charsNeeded = path1.Length + path2.Length + (needsSeparator ? 1 : 0);
+ if (destination.Length < charsNeeded)
+ return false;
+
+ path1.CopyTo(destination);
+ if (needsSeparator)
+ destination[path1.Length] = DirectorySeparatorChar;
+
+ path2.CopyTo(destination.Slice(path1.Length + (needsSeparator ? 1 : 0)));
+
+ charsWritten = charsNeeded;
+ return true;
}
- private static string CombineNoChecks(string path1, string path2, string path3, string path4)
+ public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, Span<char> destination, out int charsWritten)
{
+ charsWritten = 0;
+ if (path1.Length == 0 && path2.Length == 0 && path3.Length == 0)
+ return true;
+
if (path1.Length == 0)
- return CombineNoChecks(path2, path3, path4);
+ return TryJoin(path2, path3, destination, out charsWritten);
if (path2.Length == 0)
- return CombineNoChecks(path1, path3, path4);
+ return TryJoin(path1, path3, destination, out charsWritten);
if (path3.Length == 0)
- return CombineNoChecks(path1, path2, path4);
- if (path4.Length == 0)
- return CombineNoChecks(path1, path2, path3);
-
- if (IsPathRooted(path4))
- return path4;
- if (IsPathRooted(path3))
- return CombineNoChecks(path3, path4);
- if (IsPathRooted(path2))
- return CombineNoChecks(path2, path3, path4);
-
- bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]);
- bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]);
- bool hasSep3 = PathInternal.IsDirectoryOrVolumeSeparator(path3[path3.Length - 1]);
-
- if (hasSep1 && hasSep2 && hasSep3)
+ return TryJoin(path1, path2, destination, out charsWritten);
+
+ int neededSeparators = PathInternal.EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2) ? 0 : 1;
+ bool needsSecondSeparator = !(PathInternal.EndsInDirectorySeparator(path2) || PathInternal.StartsWithDirectorySeparator(path3));
+ if (needsSecondSeparator)
+ neededSeparators++;
+
+ int charsNeeded = path1.Length + path2.Length + path3.Length + neededSeparators;
+ if (destination.Length < charsNeeded)
+ return false;
+
+ bool result = TryJoin(path1, path2, destination, out charsWritten);
+ Debug.Assert(result, "should never fail joining first two paths");
+
+ if (needsSecondSeparator)
+ destination[charsWritten++] = DirectorySeparatorChar;
+
+ path3.CopyTo(destination.Slice(charsWritten));
+ charsWritten += path3.Length;
+
+ return true;
+ }
+
+ private static string CombineInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
+ {
+ if (first.Length == 0)
+ return second.Length == 0
+ ? string.Empty
+ : new string(second);
+
+ if (second.Length == 0)
+ return new string(first);
+
+ if (IsPathRooted(second))
+ return new string(second);
+
+ return JoinInternal(first, second);
+ }
+
+ private static string CombineInternal(string first, string second)
+ {
+ if (string.IsNullOrEmpty(first))
+ return second;
+
+ if (string.IsNullOrEmpty(second))
+ return first;
+
+ if (IsPathRooted(second.AsSpan()))
+ return second;
+
+ return JoinInternal(first, second);
+ }
+
+ private static string CombineInternal(string first, string second, string third)
+ {
+ if (string.IsNullOrEmpty(first))
+ return CombineInternal(second, third);
+ if (string.IsNullOrEmpty(second))
+ return CombineInternal(first, third);
+ if (string.IsNullOrEmpty(third))
+ return CombineInternal(first, second);
+
+ if (IsPathRooted(third.AsSpan()))
+ return third;
+ if (IsPathRooted(second.AsSpan()))
+ return CombineInternal(second, third);
+
+ return JoinInternal(first, second, third);
+ }
+
+ private static string CombineInternal(string first, string second, string third, string fourth)
+ {
+ if (string.IsNullOrEmpty(first))
+ return CombineInternal(second, third, fourth);
+ if (string.IsNullOrEmpty(second))
+ return CombineInternal(first, third, fourth);
+ if (string.IsNullOrEmpty(third))
+ return CombineInternal(first, second, fourth);
+ if (string.IsNullOrEmpty(fourth))
+ return CombineInternal(first, second, third);
+
+ if (IsPathRooted(fourth.AsSpan()))
+ return fourth;
+ if (IsPathRooted(third.AsSpan()))
+ return CombineInternal(third, fourth);
+ if (IsPathRooted(second.AsSpan()))
+ return CombineInternal(second, third, fourth);
+
+ return JoinInternal(first, second, third, fourth);
+ }
+
+ private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
+ {
+ Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");
+
+ bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
+ || PathInternal.IsDirectorySeparator(second[0]);
+
+ fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second))
{
- // Use string.Concat overload that takes four strings
- return path1 + path2 + path3 + path4;
+ return string.Create(
+ first.Length + second.Length + (hasSeparator ? 0 : 1),
+ (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
+ (destination, state) =>
+ {
+ new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
+ if (!state.HasSeparator)
+ destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.HasSeparator ? 0 : 1)));
+ });
}
- else
- {
- // string.Concat only has string-based overloads up to four arguments; after that requires allocating
- // a params string[]. Instead, try to use a cached StringBuilder.
- StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + path4.Length + 3);
+ }
- sb.Append(path1);
- if (!hasSep1)
- {
- sb.Append(PathInternal.DirectorySeparatorChar);
- }
+ private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third)
+ {
+ Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths");
- sb.Append(path2);
- if (!hasSep2)
- {
- sb.Append(PathInternal.DirectorySeparatorChar);
- }
+ bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
+ || PathInternal.IsDirectorySeparator(second[0]);
+ bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
+ || PathInternal.IsDirectorySeparator(third[0]);
- sb.Append(path3);
- if (!hasSep3)
- {
- sb.Append(PathInternal.DirectorySeparatorChar);
- }
+ fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third))
+ {
+ return string.Create(
+ first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1),
+ (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
+ Third: (IntPtr)t, ThirdLength: third.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator),
+ (destination, state) =>
+ {
+ new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
+ if (!state.FirstHasSeparator)
+ destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.FirstHasSeparator ? 0 : 1)));
+ if (!state.ThirdHasSeparator)
+ destination[destination.Length - state.ThirdLength - 1] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Third, state.ThirdLength).CopyTo(destination.Slice(destination.Length - state.ThirdLength));
+ });
+ }
+ }
+
+ private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third, ReadOnlySpan<char> fourth)
+ {
+ Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0 && fourth.Length > 0, "should have dealt with empty paths");
- sb.Append(path4);
+ bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
+ || PathInternal.IsDirectorySeparator(second[0]);
+ bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
+ || PathInternal.IsDirectorySeparator(third[0]);
+ bool fourthHasSeparator = PathInternal.IsDirectorySeparator(third[third.Length - 1])
+ || PathInternal.IsDirectorySeparator(fourth[0]);
- return StringBuilderCache.GetStringAndRelease(sb);
+ fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third), u = &MemoryMarshal.GetReference(fourth))
+ {
+ return string.Create(
+ first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1),
+ (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
+ Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength:fourth.Length,
+ FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator, FourthHasSeparator: fourthHasSeparator),
+ (destination, state) =>
+ {
+ new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
+ if (!state.FirstHasSeparator)
+ destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.FirstHasSeparator ? 0 : 1)));
+ if (!state.ThirdHasSeparator)
+ destination[state.FirstLength + state.SecondLength + (state.FirstHasSeparator ? 0 : 1)] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Third, state.ThirdLength).CopyTo(destination.Slice(state.FirstLength + state.SecondLength + (state.FirstHasSeparator ? 0 : 1) + (state.ThirdHasSeparator ? 0 : 1)));
+ if (!state.FourthHasSeparator)
+ destination[destination.Length - state.FourthLength - 1] = PathInternal.DirectorySeparatorChar;
+ new Span<char>((char*)state.Fourth, state.FourthLength).CopyTo(destination.Slice(destination.Length - state.FourthLength));
+ });
}
}
@@ -471,6 +688,93 @@ namespace System.IO
}
/// <summary>
+ /// Try to remove relative segments from the given path (without combining with a root).
+ /// </summary>
+ /// <param name="skip">Skip the specified number of characters before evaluating.</param>
+ private static string RemoveRelativeSegments(string path, int skip = 0)
+ {
+ bool flippedSeparator = false;
+
+ // Remove "//", "/./", and "/../" from the path by copying each character to the output,
+ // except the ones we're removing, such that the builder contains the normalized path
+ // at the end.
+ StringBuilder sb = StringBuilderCache.Acquire(path.Length);
+ if (skip > 0)
+ {
+ sb.Append(path, 0, skip);
+ }
+
+ for (int i = skip; i < path.Length; i++)
+ {
+ char c = path[i];
+
+ if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length)
+ {
+ // Skip this character if it's a directory separator and if the next character is, too,
+ // e.g. "parent//child" => "parent/child"
+ if (PathInternal.IsDirectorySeparator(path[i + 1]))
+ {
+ continue;
+ }
+
+ // Skip this character and the next if it's referring to the current directory,
+ // e.g. "parent/./child" => "parent/child"
+ if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) &&
+ path[i + 1] == '.')
+ {
+ i++;
+ continue;
+ }
+
+ // Skip this character and the next two if it's referring to the parent directory,
+ // e.g. "parent/child/../grandchild" => "parent/grandchild"
+ if (i + 2 < path.Length &&
+ (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) &&
+ path[i + 1] == '.' && path[i + 2] == '.')
+ {
+ // Unwind back to the last slash (and if there isn't one, clear out everything).
+ int s;
+ for (s = sb.Length - 1; s >= 0; s--)
+ {
+ if (PathInternal.IsDirectorySeparator(sb[s]))
+ {
+ sb.Length = s;
+ break;
+ }
+ }
+ if (s < 0)
+ {
+ sb.Length = 0;
+ }
+
+ i += 2;
+ continue;
+ }
+ }
+
+ // Normalize the directory separator if needed
+ if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar)
+ {
+ c = PathInternal.DirectorySeparatorChar;
+ flippedSeparator = true;
+ }
+
+ sb.Append(c);
+ }
+
+ if (flippedSeparator || sb.Length != path.Length)
+ {
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+ else
+ {
+ // We haven't changed the source path, return the original
+ StringBuilderCache.Release(sb);
+ return path;
+ }
+ }
+
+ /// <summary>
/// Create a relative path from one path to another. Paths will be resolved before calculating the difference.
/// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix).
/// </summary>
@@ -556,9 +860,6 @@ namespace System.IO
return StringBuilderCache.GetStringAndRelease(sb);
}
- // StringComparison and IsCaseSensitive are also available in PathInternal.CaseSensitivity but we are
- // too low in System.Runtime.Extensions to use it (no FileStream, etc.)
-
/// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
internal static StringComparison StringComparison
{
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
index a0dba661f..74ceed10a 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
@@ -3,8 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
namespace System.IO
{
@@ -13,197 +13,61 @@ namespace System.IO
/// </summary>
internal class PathHelper
{
- // Can't be over 8.3 and be a short name
- private const int MaxShortName = 12;
-
- private const char LastAnsi = (char)255;
- private const char Delete = (char)127;
-
/// <summary>
/// Normalize the given path.
/// </summary>
/// <remarks>
- /// Normalizes via Win32 GetFullPathName(). Will also trim initial
- /// spaces if the path is determined to be rooted.
- ///
- /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt)
+ /// Normalizes via Win32 GetFullPathName().
/// </remarks>
/// <param name="path">Path to normalize</param>
- /// <param name="checkInvalidCharacters">True to check for invalid characters</param>
- /// <param name="expandShortPaths">Attempt to expand short paths if true</param>
- /// <exception cref="ArgumentException">Thrown if the path is an illegal UNC (does not contain a full server/share) or contains illegal characters.</exception>
- /// <exception cref="PathTooLongException">Thrown if the path or a path segment exceeds the filesystem limits.</exception>
- /// <exception cref="FileNotFoundException">Thrown if Windows returns ERROR_FILE_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
- /// <exception cref="DirectoryNotFoundException">Thrown if Windows returns ERROR_PATH_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
- /// <exception cref="UnauthorizedAccessException">Thrown if Windows returns ERROR_ACCESS_DENIED. (See Win32Marshal.GetExceptionForWin32Error)</exception>
- /// <exception cref="IOException">Thrown if Windows returns an error that doesn't map to the above. (See Win32Marshal.GetExceptionForWin32Error)</exception>
+ /// <exception cref="PathTooLongException">Thrown if we have a string that is too large to fit into a UNICODE_STRING.</exception>
+ /// <exception cref="IOException">Thrown if the path is empty.</exception>
/// <returns>Normalized path</returns>
- internal static string Normalize(string path, bool checkInvalidCharacters, bool expandShortPaths)
+ internal static string Normalize(string path)
{
- // Get the full path
- StringBuffer fullPath = new StringBuffer(PathInternal.MaxShortPath);
-
- try
- {
- GetFullPathName(path, ref fullPath);
-
- // Checking path validity used to happen before getting the full path name. To avoid additional input allocation
- // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that
- // used to get kicked back (notably segments with invalid characters might get removed via "..").
- //
- // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to
- // expand short file names.
-
- // Scan the path for:
- //
- // - Illegal path characters.
- // - Invalid UNC paths like \\, \\server, \\server\.
-
- // As the path could be > 30K, we'll combine the validity scan. None of these checks are performed by the Win32
- // GetFullPathName() API.
-
- bool possibleShortPath = false;
- bool foundTilde = false;
-
- // We can get UNCs as device paths through this code (e.g. \\.\UNC\), we won't validate them as there isn't
- // an easy way to normalize without extensive cost (we'd have to hunt down the canonical name for any device
- // path that contains UNC or to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\,
- // \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc.
- bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\';
- bool isDevice = PathInternal.IsDevice(ref fullPath);
- bool possibleBadUnc = specialPath && !isDevice;
- int index = specialPath ? 2 : 0;
- int lastSeparator = specialPath ? 1 : 0;
- int segmentLength;
- char current;
-
- while (index < fullPath.Length)
- {
- current = fullPath[index];
-
- // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~'
- if (current < '?' || current == '\\' || current == '|' || current == '~')
- {
- switch (current)
- {
- case '|':
- case '>':
- case '<':
- case '\"':
- if (checkInvalidCharacters) throw new ArgumentException(SR.Argument_InvalidPathChars);
- foundTilde = false;
- break;
- case '~':
- foundTilde = true;
- break;
- case '\\':
- segmentLength = index - lastSeparator - 1;
- lastSeparator = index;
-
- if (foundTilde)
- {
- if (segmentLength <= MaxShortName)
- {
- // Possibly a short path.
- possibleShortPath = true;
- }
-
- foundTilde = false;
- }
-
- if (possibleBadUnc)
- {
- // If we're at the end of the path and this is the first separator, we're missing the share.
- // Otherwise we're good, so ignore UNC tracking from here.
- if (index == fullPath.Length - 1)
- throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString()));
- else
- possibleBadUnc = false;
- }
-
- break;
-
- default:
- if (checkInvalidCharacters && current < ' ') throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
- break;
- }
- }
-
- index++;
- }
-
- if (possibleBadUnc)
- throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString()));
-
- segmentLength = fullPath.Length - lastSeparator - 1;
+ Span<char> initialBuffer = stackalloc char[PathInternal.MaxShortPath];
+ ValueStringBuilder builder = new ValueStringBuilder(initialBuffer);
- if (foundTilde && segmentLength <= MaxShortName)
- possibleShortPath = true;
+ // Get the full path
+ GetFullPathName(path, ref builder);
- // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but
- // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide.
- if (expandShortPaths && possibleShortPath)
- {
- return TryExpandShortFileName(ref fullPath, originalPath: path);
- }
- else
- {
- if (fullPath.Length == path.Length && fullPath.StartsWith(path))
- {
- // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder.
- return path;
- }
- else
- {
- return fullPath.ToString();
- }
- }
- }
- finally
- {
- // Clear the buffer
- fullPath.Free();
- }
- }
+ // If we have the exact same string we were passed in, don't allocate another string.
+ // TryExpandShortName does this input identity check.
+ string result = builder.AsSpan().Contains('~')
+ ? TryExpandShortFileName(ref builder, originalPath: path)
+ : builder.AsSpan().Equals(path.AsSpan()) ? path : builder.ToString();
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsDosUnc(ref StringBuffer buffer)
- {
- return !PathInternal.IsDevice(ref buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
+ // Clear the buffer
+ builder.Dispose();
+ return result;
}
- private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath)
+ private static void GetFullPathName(string path, ref ValueStringBuilder builder)
{
// If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
// it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));
- // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
- int startIndex = PathInternal.PathStartSkip(path);
-
- fixed (char* pathStart = path)
+ uint result = 0;
+ while ((result = Interop.Kernel32.GetFullPathNameW(path, (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity)
{
- uint result = 0;
- while ((result = Interop.Kernel32.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.Capacity, fullPath.UnderlyingArray, IntPtr.Zero)) > fullPath.Capacity)
- {
- // Reported size is greater than the buffer size. Increase the capacity.
- fullPath.EnsureCapacity(checked((int)result));
- }
-
- if (result == 0)
- {
- // Failure, get the error and throw
- int errorCode = Marshal.GetLastWin32Error();
- if (errorCode == 0)
- errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
- }
+ // Reported size is greater than the buffer size. Increase the capacity.
+ builder.EnsureCapacity(checked((int)result));
+ }
- fullPath.Length = checked((int)result);
+ if (result == 0)
+ {
+ // Failure, get the error and throw
+ int errorCode = Marshal.GetLastWin32Error();
+ if (errorCode == 0)
+ errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
}
+
+ builder.Length = (int)result;
}
- private static int GetInputBuffer(ref StringBuffer content, bool isDosUnc, ref StringBuffer buffer)
+ private static int PrependDevicePathChars(ref ValueStringBuilder content, bool isDosUnc, ref ValueStringBuilder buffer)
{
int length = content.Length;
@@ -212,37 +76,34 @@ namespace System.IO
: PathInternal.DevicePrefixLength;
buffer.EnsureCapacity(length + 1);
+ buffer.Length = 0;
if (isDosUnc)
{
- // Put the extended UNC prefix (\\?\UNC\) in front of the path
- buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix);
+ // Is a \\Server\Share, put \\?\UNC\ in the front
+ buffer.Append(PathInternal.UncExtendedPathPrefix);
- // Copy the source buffer over after the existing UNC prefix
- content.CopyTo(
- bufferIndex: PathInternal.UncPrefixLength,
- destination: ref buffer,
- destinationIndex: PathInternal.UncExtendedPrefixLength,
- count: content.Length - PathInternal.UncPrefixLength);
+ // Copy Server\Share\... over to the buffer
+ buffer.Append(content.AsSpan().Slice(PathInternal.UncPrefixLength));
// Return the prefix difference
return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
}
else
{
- int prefixSize = PathInternal.ExtendedPathPrefix.Length;
- buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix);
- content.CopyTo(bufferIndex: 0, destination: ref buffer, destinationIndex: prefixSize, count: content.Length);
- return prefixSize;
+ // Not an UNC, put the \\?\ prefix in front, then the original string
+ buffer.Append(PathInternal.ExtendedPathPrefix);
+ buffer.Append(content.AsSpan());
+ return PathInternal.DevicePrefixLength;
}
}
- private static string TryExpandShortFileName(ref StringBuffer outputBuffer, string originalPath)
+ private static string TryExpandShortFileName(ref ValueStringBuilder outputBuilder, string originalPath)
{
// We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To
// avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls.
- Debug.Assert(!PathInternal.IsPartiallyQualified(ref outputBuffer), "should have resolved by now");
+ Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuilder.AsSpan()), "should have resolved by now");
// We'll have one of a few cases by now (the normalized path will have already:
//
@@ -250,135 +111,120 @@ namespace System.IO
// 2. Dos UNC (\\Server\Share)
// 3. Dos device path (\\.\C:\, \\?\C:\)
//
- // We want to put the extended syntax on the front if it doesn't already have it, which may mean switching from \\.\.
+ // We want to put the extended syntax on the front if it doesn't already have it (for long path support and speed), which may mean switching from \\.\.
//
// Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is).
- int rootLength = PathInternal.GetRootLength(ref outputBuffer);
- bool isDevice = PathInternal.IsDevice(ref outputBuffer);
+ int rootLength = PathInternal.GetRootLength(outputBuilder.AsSpan());
+ bool isDevice = PathInternal.IsDevice(outputBuilder.AsSpan());
- StringBuffer inputBuffer = new StringBuffer(0);
- try
- {
- bool isDosUnc = false;
- int rootDifference = 0;
- bool wasDotDevice = false;
+ // As this is a corner case we're not going to add a stackalloc here to keep the stack pressure down.
+ ValueStringBuilder inputBuilder = new ValueStringBuilder();
- // Add the extended prefix before expanding to allow growth over MAX_PATH
- if (isDevice)
- {
- // We have one of the following (\\?\ or \\.\)
- inputBuffer.Append(ref outputBuffer);
+ bool isDosUnc = false;
+ int rootDifference = 0;
+ bool wasDotDevice = false;
- if (outputBuffer[2] == '.')
- {
- wasDotDevice = true;
- inputBuffer[2] = '?';
- }
- }
- else
+ // Add the extended prefix before expanding to allow growth over MAX_PATH
+ if (isDevice)
+ {
+ // We have one of the following (\\?\ or \\.\)
+ inputBuilder.Append(outputBuilder.AsSpan());
+
+ if (outputBuilder[2] == '.')
{
- isDosUnc = IsDosUnc(ref outputBuffer);
- rootDifference = GetInputBuffer(ref outputBuffer, isDosUnc, ref inputBuffer);
+ wasDotDevice = true;
+ inputBuilder[2] = '?';
}
+ }
+ else
+ {
+ isDosUnc = !PathInternal.IsDevice(outputBuilder.AsSpan()) && outputBuilder.Length > 1 && outputBuilder[0] == '\\' && outputBuilder[1] == '\\';
+ rootDifference = PrependDevicePathChars(ref outputBuilder, isDosUnc, ref inputBuilder);
+ }
- rootLength += rootDifference;
- int inputLength = inputBuffer.Length;
+ rootLength += rootDifference;
+ int inputLength = inputBuilder.Length;
- bool success = false;
- int foundIndex = inputBuffer.Length - 1;
+ bool success = false;
+ int foundIndex = inputBuilder.Length - 1;
- while (!success)
- {
- uint result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
+ // Need to null terminate the input builder
+ inputBuilder.Append('\0');
- // Replace any temporary null we added
- if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';
+ while (!success)
+ {
+ uint result = Interop.Kernel32.GetLongPathNameW(ref inputBuilder.GetPinnableReference(), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity);
- if (result == 0)
+ // Replace any temporary null we added
+ if (inputBuilder[foundIndex] == '\0') inputBuilder[foundIndex] = '\\';
+
+ if (result == 0)
+ {
+ // Look to see if we couldn't find the file
+ int error = Marshal.GetLastWin32Error();
+ if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND)
{
- // Look to see if we couldn't find the file
- int error = Marshal.GetLastWin32Error();
- if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND)
- {
- // Some other failure, give up
- break;
- }
-
- // We couldn't find the path at the given index, start looking further back in the string.
- foundIndex--;
-
- for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ;
- if (foundIndex == rootLength)
- {
- // Can't trim the path back any further
- break;
- }
- else
- {
- // Temporarily set a null in the string to get Windows to look further up the path
- inputBuffer[foundIndex] = '\0';
- }
+ // Some other failure, give up
+ break;
}
- else if (result > outputBuffer.Capacity)
+
+ // We couldn't find the path at the given index, start looking further back in the string.
+ foundIndex--;
+
+ for (; foundIndex > rootLength && inputBuilder[foundIndex] != '\\'; foundIndex--) ;
+ if (foundIndex == rootLength)
{
- // Not enough space. The result count for this API does not include the null terminator.
- outputBuffer.EnsureCapacity(checked((int)result));
- result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
+ // Can't trim the path back any further
+ break;
}
else
{
- // Found the path
- success = true;
- outputBuffer.Length = checked((int)result);
- if (foundIndex < inputLength - 1)
- {
- // It was a partial find, put the non-existent part of the path back
- outputBuffer.Append(ref inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
- }
+ // Temporarily set a null in the string to get Windows to look further up the path
+ inputBuilder[foundIndex] = '\0';
}
}
-
- // Strip out the prefix and return the string
- ref StringBuffer bufferToUse = ref Choose(success, ref outputBuffer, ref inputBuffer);
-
- // Switch back from \\?\ to \\.\ if necessary
- if (wasDotDevice)
- bufferToUse[2] = '.';
-
- string returnValue = null;
-
- int newLength = (int)(bufferToUse.Length - rootDifference);
- if (isDosUnc)
- {
- // Need to go from \\?\UNC\ to \\?\UN\\
- bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
- }
-
- // We now need to strip out any added characters at the front of the string
- if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
+ else if (result > outputBuilder.Capacity)
{
- // Use the original path to avoid allocating
- returnValue = originalPath;
+ // Not enough space. The result count for this API does not include the null terminator.
+ outputBuilder.EnsureCapacity(checked((int)result));
+ result = Interop.Kernel32.GetLongPathNameW(ref inputBuilder.GetPinnableReference(), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity);
}
else
{
- returnValue = bufferToUse.Substring(rootDifference, newLength);
+ // Found the path
+ success = true;
+ outputBuilder.Length = checked((int)result);
+ if (foundIndex < inputLength - 1)
+ {
+ // It was a partial find, put the non-existent part of the path back
+ outputBuilder.Append(inputBuilder.AsSpan().Slice(foundIndex, inputBuilder.Length - foundIndex));
+ }
}
-
- return returnValue;
- }
- finally
- {
- inputBuffer.Free();
}
- }
- // Helper method to workaround lack of operator ? support for ref values
- private static ref StringBuffer Choose(bool condition, ref StringBuffer s1, ref StringBuffer s2)
- {
- if (condition) return ref s1;
- else return ref s2;
+ // Need to trim out the trailing separator in the input builder
+ inputBuilder.Length = inputBuilder.Length - 1;
+
+ // If we were able to expand the path, use it, otherwise use the original full path result
+ ref ValueStringBuilder builderToUse = ref (success ? ref outputBuilder : ref inputBuilder);
+
+ // Switch back from \\?\ to \\.\ if necessary
+ if (wasDotDevice)
+ builderToUse[2] = '.';
+
+ // Change from \\?\UNC\ to \\?\UN\\ if needed
+ if (isDosUnc)
+ builderToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
+
+ // Strip out any added characters at the front of the string
+ ReadOnlySpan<char> output = builderToUse.AsSpan().Slice(rootDifference);
+
+ string returnValue = output.Equals(originalPath.AsSpan())
+ ? originalPath : new string(output);
+
+ inputBuilder.Dispose();
+ return returnValue;
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs
index 2f65a4252..fae309be5 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs
@@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Text;
+using System.Runtime.InteropServices;
namespace System.IO
{
@@ -22,7 +23,7 @@ namespace System.IO
internal const string ParentDirectoryPrefix = @"../";
- internal static int GetRootLength(string path)
+ internal static int GetRootLength(ReadOnlySpan<char> path)
{
return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0;
}
@@ -40,7 +41,8 @@ namespace System.IO
/// </summary>
internal static string NormalizeDirectorySeparators(string path)
{
- if (string.IsNullOrEmpty(path)) return path;
+ if (string.IsNullOrEmpty(path))
+ return path;
// Make a pass to see if we need to normalize so we can potentially skip allocating
bool normalized = true;
@@ -55,7 +57,8 @@ namespace System.IO
}
}
- if (normalized) return path;
+ if (normalized)
+ return path;
StringBuilder builder = new StringBuilder(path.Length);
@@ -73,32 +76,14 @@ namespace System.IO
return builder.ToString();
}
-
- /// <summary>
- /// Returns true if the character is a directory or volume separator.
- /// </summary>
- /// <param name="ch">The character to test.</param>
- internal static bool IsDirectoryOrVolumeSeparator(char ch)
- {
- // The directory separator, volume separator, and the alternate directory
- // separator should be the same on Unix, so we only need to check one.
- Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
- Debug.Assert(DirectorySeparatorChar == VolumeSeparatorChar);
- return ch == DirectorySeparatorChar;
- }
- internal static bool IsPartiallyQualified(string path)
+ internal static bool IsPartiallyQualified(ReadOnlySpan<char> path)
{
// This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative)
// As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified.
return !Path.IsPathRooted(path);
}
- internal static string TrimEndingDirectorySeparator(string path) =>
- path.Length > 1 && IsDirectorySeparator(path[path.Length - 1]) ? // exclude root "/"
- path.Substring(0, path.Length - 1) :
- path;
-
/// <summary>
/// Returns true if the path is effectively empty for the current OS.
/// For unix, this is empty or null. For Windows, this is empty, null, or
@@ -108,5 +93,10 @@ namespace System.IO
{
return string.IsNullOrEmpty(path);
}
+
+ internal static bool IsEffectivelyEmpty(ReadOnlySpan<char> path)
+ {
+ return path.IsEmpty;
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.StringBuffer.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.StringBuffer.cs
deleted file mode 100644
index 84953df37..000000000
--- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.StringBuffer.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace System.IO
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- /// <summary>
- /// Returns true if the path uses the extended syntax (\\?\)
- /// </summary>
- internal static bool IsExtended(ref StringBuffer path)
- {
- // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
- // Skipping of normalization will *only* occur if back slashes ('\') are used.
- return path.Length >= DevicePrefixLength
- && path[0] == '\\'
- && (path[1] == '\\' || path[1] == '?')
- && path[2] == '?'
- && path[3] == '\\';
- }
-
- /// <summary>
- /// Gets the length of the root of the path (drive, share, etc.).
- /// </summary>
- internal unsafe static int GetRootLength(ref StringBuffer path)
- {
- if (path.Length == 0) return 0;
-
- fixed (char* value = path.UnderlyingArray)
- {
- return GetRootLength(value, path.Length);
- }
- }
-
- /// <summary>
- /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
- /// </summary>
- internal static bool IsDevice(ref StringBuffer path)
- {
- // If the path begins with any two separators is will be recognized and normalized and prepped with
- // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
- return IsExtended(ref path)
- ||
- (
- path.Length >= DevicePrefixLength
- && IsDirectorySeparator(path[0])
- && IsDirectorySeparator(path[1])
- && (path[2] == '.' || path[2] == '?')
- && IsDirectorySeparator(path[3])
- );
- }
-
- /// <summary>
- /// Returns true if the path specified is relative to the current drive or working directory.
- /// Returns false if the path is fixed to a specific drive or UNC path. This method does no
- /// validation of the path (URIs will be returned as relative as a result).
- /// </summary>
- /// <remarks>
- /// Handles paths that use the alternate directory separator. It is a frequent mistake to
- /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
- /// "C:a" is drive relative- meaning that it will be resolved against the current directory
- /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
- /// will not be used to modify the path).
- /// </remarks>
- internal static bool IsPartiallyQualified(ref StringBuffer path)
- {
- if (path.Length < 2)
- {
- // It isn't fixed, it must be relative. There is no way to specify a fixed
- // path with one character (or less).
- return true;
- }
-
- if (IsDirectorySeparator(path[0]))
- {
- // There is no valid way to specify a relative path with two initial slashes or
- // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
- return !(path[1] == '?' || IsDirectorySeparator(path[1]));
- }
-
- // The only way to specify a fixed path that doesn't begin with two slashes
- // is the drive, colon, slash format- i.e. C:\
- return !((path.Length >= 3)
- && (path[1] == VolumeSeparatorChar)
- && IsDirectorySeparator(path[2]));
- }
- }
-}
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs
index f315f43fd..81d51ba4b 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
@@ -71,7 +70,36 @@ namespace System.IO
return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z'));
}
+ private static bool EndsWithPeriodOrSpace(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return false;
+
+ char c = path[path.Length - 1];
+ return c == ' ' || c == '.';
+ }
+
+ /// <summary>
+ /// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative,
+ /// AND the path is more than 259 characters. (> MAX_PATH + null). This will also insert the extended
+ /// prefix if the path ends with a period or a space. Trailing periods and spaces are normally eaten
+ /// away from paths during normalization, but if we see such a path at this point it should be
+ /// normalized and has retained the final characters. (Typically from one of the *Info classes)
+ /// </summary>
+ internal static string EnsureExtendedPrefixIfNeeded(string path)
+ {
+ if (path != null && (path.Length >= MaxShortPath || EndsWithPeriodOrSpace(path)))
+ {
+ return EnsureExtendedPrefix(path);
+ }
+ else
+ {
+ return path;
+ }
+ }
+
/// <summary>
+ /// DO NOT USE- Use EnsureExtendedPrefixIfNeeded. This will be removed shortly.
/// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative,
/// AND the path is more than 259 characters. (> MAX_PATH + null)
/// </summary>
@@ -115,7 +143,7 @@ namespace System.IO
/// <summary>
/// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
/// </summary>
- internal static bool IsDevice(string path)
+ internal static bool IsDevice(ReadOnlySpan<char> path)
{
// If the path begins with any two separators is will be recognized and normalized and prepped with
// "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
@@ -131,11 +159,24 @@ namespace System.IO
}
/// <summary>
+ /// Returns true if the path is a device UNC (\\?\UNC\, \\.\UNC\)
+ /// </summary>
+ internal static bool IsDeviceUNC(ReadOnlySpan<char> path)
+ {
+ return path.Length >= UncExtendedPrefixLength
+ && IsDevice(path)
+ && IsDirectorySeparator(path[7])
+ && path[4] == 'U'
+ && path[5] == 'N'
+ && path[6] == 'C';
+ }
+
+ /// <summary>
/// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the
/// path matches exactly (cannot use alternate directory separators) Windows will skip normalization
/// and path length checks.
/// </summary>
- internal static bool IsExtended(string path)
+ internal static bool IsExtended(ReadOnlySpan<char> path)
{
// While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
// Skipping of normalization will *only* occur if back slashes ('\') are used.
@@ -149,7 +190,7 @@ namespace System.IO
/// <summary>
/// Check for known wildcard characters. '*' and '?' are the most common ones.
/// </summary>
- internal static bool HasWildCardCharacters(string path)
+ internal static bool HasWildCardCharacters(ReadOnlySpan<char> path)
{
// Question mark is part of dos device syntax so we have to skip if we are
int startIndex = IsDevice(path) ? ExtendedPathPrefix.Length : 0;
@@ -172,67 +213,72 @@ namespace System.IO
/// <summary>
/// Gets the length of the root of the path (drive, share, etc.).
/// </summary>
- internal unsafe static int GetRootLength(string path)
- {
- fixed (char* value = path)
- {
- return GetRootLength(value, path.Length);
- }
- }
-
- private unsafe static int GetRootLength(char* path, int pathLength)
+ internal static int GetRootLength(ReadOnlySpan<char> path)
{
+ int pathLength = path.Length;
int i = 0;
- int volumeSeparatorLength = 2; // Length to the colon "C:"
- int uncRootLength = 2; // Length to the start of the server name "\\"
- bool extendedSyntax = StartsWithOrdinal(path, pathLength, ExtendedPathPrefix);
- bool extendedUncSyntax = StartsWithOrdinal(path, pathLength, UncExtendedPathPrefix);
- if (extendedSyntax)
+ bool deviceSyntax = IsDevice(path);
+ bool deviceUnc = deviceSyntax && IsDeviceUNC(path);
+
+ if ((!deviceSyntax || deviceUnc) && pathLength > 0 && IsDirectorySeparator(path[0]))
{
- // Shift the position we look for the root from to account for the extended prefix
- if (extendedUncSyntax)
+ // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo")
+ if (deviceUnc || (pathLength > 1 && IsDirectorySeparator(path[1])))
{
- // "\\" -> "\\?\UNC\"
- uncRootLength = UncExtendedPathPrefix.Length;
+ // UNC (\\?\UNC\ or \\), scan past server\share
+
+ // Start past the prefix ("\\" or "\\?\UNC\")
+ i = deviceUnc ? UncExtendedPrefixLength : UncPrefixLength;
+
+ // Skip two separators at most
+ int n = 2;
+ while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0))
+ i++;
}
else
{
- // "C:" -> "\\?\C:"
- volumeSeparatorLength += ExtendedPathPrefix.Length;
+ // Current drive rooted (e.g. "\foo")
+ i = 1;
}
}
-
- if ((!extendedSyntax || extendedUncSyntax) && pathLength > 0 && IsDirectorySeparator(path[0]))
+ else if (deviceSyntax)
{
- // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo")
-
- i = 1; // Drive rooted (\foo) is one character
- if (extendedUncSyntax || (pathLength > 1 && IsDirectorySeparator(path[1])))
- {
- // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most
- // (e.g. to \\?\UNC\Server\Share or \\Server\Share\)
- i = uncRootLength;
- int n = 2; // Maximum separators to skip
- while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++;
- }
+ // Device path (e.g. "\\?\.", "\\.\")
+ // Skip any characters following the prefix that aren't a separator
+ i = DevicePrefixLength;
+ while (i < pathLength && !IsDirectorySeparator(path[i]))
+ i++;
+
+ // If there is another separator take it, as long as we have had at least one
+ // non-separator after the prefix (e.g. don't take "\\?\\", but take "\\?\a\")
+ if (i < pathLength && i > DevicePrefixLength && IsDirectorySeparator(path[i]))
+ i++;
}
- else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == VolumeSeparatorChar)
+ else if (pathLength >= 2
+ && path[1] == VolumeSeparatorChar
+ && IsValidDriveChar(path[0]))
{
- // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:)
- // If the colon is followed by a directory separator, move past it
- i = volumeSeparatorLength;
- if (pathLength >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++;
+ // Valid drive specified path ("C:", "D:", etc.)
+ i = 2;
+
+ // If the colon is followed by a directory separator, move past it (e.g "C:\")
+ if (pathLength > 2 && IsDirectorySeparator(path[2]))
+ i++;
}
+
return i;
}
- private unsafe static bool StartsWithOrdinal(char* source, int sourceLength, string value)
+ private static bool StartsWithOrdinal(ReadOnlySpan<char> source, string value)
{
- if (sourceLength < value.Length) return false;
+ if (source.Length < value.Length)
+ return false;
+
for (int i = 0; i < value.Length; i++)
{
- if (value[i] != source[i]) return false;
+ if (value[i] != source[i])
+ return false;
}
return true;
}
@@ -249,7 +295,7 @@ namespace System.IO
/// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
/// will not be used to modify the path).
/// </remarks>
- internal static bool IsPartiallyQualified(string path)
+ internal static bool IsPartiallyQualified(ReadOnlySpan<char> path)
{
if (path.Length < 2)
{
@@ -276,29 +322,6 @@ namespace System.IO
}
/// <summary>
- /// Returns the characters to skip at the start of the path if it starts with space(s) and a drive or directory separator.
- /// (examples are " C:", " \")
- /// This is a legacy behavior of Path.GetFullPath().
- /// </summary>
- /// <remarks>
- /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip.
- /// </remarks>
- internal static int PathStartSkip(string path)
- {
- int startIndex = 0;
- while (startIndex < path.Length && path[startIndex] == ' ') startIndex++;
-
- if (startIndex > 0 && (startIndex < path.Length && IsDirectorySeparator(path[startIndex]))
- || (startIndex + 1 < path.Length && path[startIndex + 1] == ':' && IsValidDriveChar(path[startIndex])))
- {
- // Go ahead and skip spaces as we're either " C:" or " \"
- return startIndex;
- }
-
- return 0;
- }
-
- /// <summary>
/// True if the given character is a directory separator.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -341,34 +364,33 @@ namespace System.IO
/// </remarks>
internal static string NormalizeDirectorySeparators(string path)
{
- if (string.IsNullOrEmpty(path)) return path;
+ if (string.IsNullOrEmpty(path))
+ return path;
char current;
- int start = PathStartSkip(path);
- if (start == 0)
- {
- // Make a pass to see if we need to normalize so we can potentially skip allocating
- bool normalized = true;
+ // Make a pass to see if we need to normalize so we can potentially skip allocating
+ bool normalized = true;
- for (int i = 0; i < path.Length; i++)
+ for (int i = 0; i < path.Length; i++)
+ {
+ current = path[i];
+ if (IsDirectorySeparator(current)
+ && (current != DirectorySeparatorChar
+ // Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
+ || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))))
{
- current = path[i];
- if (IsDirectorySeparator(current)
- && (current != DirectorySeparatorChar
- // Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
- || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))))
- {
- normalized = false;
- break;
- }
+ normalized = false;
+ break;
}
-
- if (normalized) return path;
}
+ if (normalized)
+ return path;
+
StringBuilder builder = new StringBuilder(path.Length);
+ int start = 0;
if (IsDirectorySeparator(path[start]))
{
start++;
@@ -399,22 +421,13 @@ namespace System.IO
}
/// <summary>
- /// Returns true if the character is a directory or volume separator.
- /// </summary>
- /// <param name="ch">The character to test.</param>
- internal static bool IsDirectoryOrVolumeSeparator(char ch)
- {
- return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch;
- }
-
- /// <summary>
/// Returns true if the path is effectively empty for the current OS.
/// For unix, this is empty or null. For Windows, this is empty, null, or
/// just spaces ((char)32).
/// </summary>
- internal static bool IsEffectivelyEmpty(string path)
+ internal static bool IsEffectivelyEmpty(ReadOnlySpan<char> path)
{
- if (string.IsNullOrEmpty(path))
+ if (path.IsEmpty)
return true;
foreach (char c in path)
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
index bfd69e925..eb06c2608 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
@@ -2,45 +2,37 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Diagnostics;
-using System.Text;
-
namespace System.IO
{
/// <summary>Contains internal path helpers that are shared between many projects.</summary>
internal static partial class PathInternal
{
/// <summary>
- /// Returns the start index of the filename
- /// in the given path, or 0 if no directory
- /// or volume separator is found.
+ /// Returns true if the path ends in a directory separator.
/// </summary>
- /// <param name="path">The path in which to find the index of the filename.</param>
- /// <remarks>
- /// This method returns path.Length for
- /// inputs like "/usr/foo/" on Unix. As such,
- /// it is not safe for being used to index
- /// the string without additional verification.
- /// </remarks>
- internal static int FindFileNameIndex(string path)
- {
- Debug.Assert(path != null);
-
- for (int i = path.Length - 1; i >= 0; i--)
- {
- char ch = path[i];
- if (IsDirectoryOrVolumeSeparator(ch))
- return i + 1;
- }
-
- return 0; // the whole path is the filename
- }
+ internal static bool EndsInDirectorySeparator(ReadOnlySpan<char> path)
+ => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]);
/// <summary>
- /// Returns true if the path ends in a directory separator.
+ /// Returns true if the path starts in a directory separator.
/// </summary>
- internal static bool EndsInDirectorySeparator(string path) =>
- !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]);
+ internal static bool StartsWithDirectorySeparator(ReadOnlySpan<char> path) => path.Length > 0 && IsDirectorySeparator(path[0]);
+
+ internal static string EnsureTrailingSeparator(string path)
+ => EndsInDirectorySeparator(path) ? path : path + DirectorySeparatorCharAsString;
+
+ internal static string TrimEndingDirectorySeparator(string path) =>
+ EndsInDirectorySeparator(path) && !IsRoot(path) ?
+ path.Substring(0, path.Length - 1) :
+ path;
+
+ internal static ReadOnlySpan<char> TrimEndingDirectorySeparator(ReadOnlySpan<char> path) =>
+ EndsInDirectorySeparator(path) && !IsRoot(path) ?
+ path.Slice(0, path.Length - 1) :
+ path;
+
+ internal static bool IsRoot(ReadOnlySpan<char> path)
+ => path.Length == GetRootLength(path);
/// <summary>
/// Get the common path length from the start of the string.
@@ -114,26 +106,5 @@ namespace System.IO
length: firstRootLength,
comparisonType: comparisonType) == 0;
}
-
- /// <summary>
- /// Returns false for ".." unless it is specified as a part of a valid File/Directory name.
- /// (Used to avoid moving up directories.)
- ///
- /// Valid: a..b abc..d
- /// Invalid: ..ab ab.. .. abc..d\abc..
- /// </summary>
- internal static void CheckSearchPattern(string searchPattern)
- {
- int index;
- while ((index = searchPattern.IndexOf("..", StringComparison.Ordinal)) != -1)
- {
- // Terminal ".." . Files names cannot end in ".."
- if (index + 2 == searchPattern.Length
- || IsDirectorySeparator(searchPattern[index + 2]))
- throw new ArgumentException(SR.Format(SR.Arg_InvalidSearchPattern, searchPattern));
-
- searchPattern = searchPattern.Substring(index + 2);
- }
- }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs b/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs
new file mode 100644
index 000000000..22ec6e645
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs
@@ -0,0 +1,1422 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO
+{
+ // This class implements a TextReader for reading characters to a Stream.
+ // This is designed for character input in a particular Encoding,
+ // whereas the Stream class is designed for byte input and output.
+ public class StreamReader : TextReader
+ {
+ // StreamReader.Null is threadsafe.
+ public new static readonly StreamReader Null = new NullStreamReader();
+
+ // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well
+ // perf-wise. On even a 40 MB text file, any perf loss by using a 4K
+ // buffer is negated by the win of allocating a smaller byte[], which
+ // saves construction time. This does break adaptive buffering,
+ // but this is slightly faster.
+ private const int DefaultBufferSize = 1024; // Byte buffer size
+ private const int DefaultFileStreamBufferSize = 4096;
+ private const int MinBufferSize = 128;
+
+ private Stream _stream;
+ private Encoding _encoding;
+ private Decoder _decoder;
+ private byte[] _byteBuffer;
+ private char[] _charBuffer;
+ private int _charPos;
+ private int _charLen;
+ // Record the number of valid bytes in the byteBuffer, for a few checks.
+ private int _byteLen;
+ // This is used only for preamble detection
+ private int _bytePos;
+
+ // This is the maximum number of chars we can get from one call to
+ // ReadBuffer. Used so ReadBuffer can tell when to copy data into
+ // a user's char[] directly, instead of our internal char[].
+ private int _maxCharsPerBuffer;
+
+ // We will support looking for byte order marks in the stream and trying
+ // to decide what the encoding might be from the byte order marks, IF they
+ // exist. But that's all we'll do.
+ private bool _detectEncoding;
+
+ // Whether we must still check for the encoding's given preamble at the
+ // beginning of this file.
+ private bool _checkPreamble;
+
+ // Whether the stream is most likely not going to give us back as much
+ // data as we want the next time we call it. We must do the computation
+ // before we do any byte order mark handling and save the result. Note
+ // that we need this to allow users to handle streams used for an
+ // interactive protocol, where they block waiting for the remote end
+ // to send a response, like logging in on a Unix machine.
+ private bool _isBlocked;
+
+ // The intent of this field is to leave open the underlying stream when
+ // disposing of this StreamReader. A name like _leaveOpen is better,
+ // but this type is serializable, and this field's name was _closable.
+ private bool _closable; // Whether to close the underlying stream.
+
+ // We don't guarantee thread safety on StreamReader, but we should at
+ // least prevent users from trying to read anything while an Async
+ // read from the same thread is in progress.
+ private volatile Task _asyncReadTask;
+
+ private void CheckAsyncTaskInProgress()
+ {
+ // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety.
+ // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress.
+
+ Task t = _asyncReadTask;
+
+ if (t != null && !t.IsCompleted)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
+ }
+ }
+
+ // StreamReader by default will ignore illegal UTF8 characters. We don't want to
+ // throw here because we want to be able to read ill-formed data without choking.
+ // The high level goal is to be tolerant of encoding errors when we read and very strict
+ // when we write. Hence, default StreamWriter encoding will throw on error.
+
+ internal StreamReader()
+ {
+ }
+
+ public StreamReader(Stream stream)
+ : this(stream, true)
+ {
+ }
+
+ public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
+ : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
+ {
+ }
+
+ public StreamReader(Stream stream, Encoding encoding)
+ : this(stream, encoding, true, DefaultBufferSize, false)
+ {
+ }
+
+ public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
+ : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
+ {
+ }
+
+ // Creates a new StreamReader for the given stream. The
+ // character encoding is set by encoding and the buffer size,
+ // in number of 16-bit characters, is set by bufferSize.
+ //
+ // Note that detectEncodingFromByteOrderMarks is a very
+ // loose attempt at detecting the encoding by looking at the first
+ // 3 bytes of the stream. It will recognize UTF-8, little endian
+ // unicode, and big endian unicode text, but that's it. If neither
+ // of those three match, it will use the Encoding you provided.
+ //
+ public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
+ : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false)
+ {
+ }
+
+ public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
+ {
+ if (stream == null || encoding == null)
+ {
+ throw new ArgumentNullException(stream == null ? nameof(stream) : nameof(encoding));
+ }
+ if (!stream.CanRead)
+ {
+ throw new ArgumentException(SR.Argument_StreamNotReadable);
+ }
+ if (bufferSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+ }
+
+ Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen);
+ }
+
+ public StreamReader(string path)
+ : this(path, true)
+ {
+ }
+
+ public StreamReader(string path, bool detectEncodingFromByteOrderMarks)
+ : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize)
+ {
+ }
+
+ public StreamReader(string path, Encoding encoding)
+ : this(path, encoding, true, DefaultBufferSize)
+ {
+ }
+
+ public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
+ : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
+ {
+ }
+
+ public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ if (encoding == null)
+ throw new ArgumentNullException(nameof(encoding));
+ if (path.Length == 0)
+ throw new ArgumentException(SR.Argument_EmptyPath);
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+
+ Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read,
+ DefaultFileStreamBufferSize, FileOptions.SequentialScan);
+ Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen: false);
+ }
+
+ private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
+ {
+ _stream = stream;
+ _encoding = encoding;
+ _decoder = encoding.GetDecoder();
+ if (bufferSize < MinBufferSize)
+ {
+ bufferSize = MinBufferSize;
+ }
+
+ _byteBuffer = new byte[bufferSize];
+ _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
+ _charBuffer = new char[_maxCharsPerBuffer];
+ _byteLen = 0;
+ _bytePos = 0;
+ _detectEncoding = detectEncodingFromByteOrderMarks;
+ _checkPreamble = encoding.Preamble.Length > 0;
+ _isBlocked = false;
+ _closable = !leaveOpen;
+ }
+
+ // Init used by NullStreamReader, to delay load encoding
+ internal void Init(Stream stream)
+ {
+ _stream = stream;
+ _closable = true;
+ }
+
+ public override void Close()
+ {
+ Dispose(true);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // Dispose of our resources if this StreamReader is closable.
+ // Note that Console.In should be left open.
+ try
+ {
+ // Note that Stream.Close() can potentially throw here. So we need to
+ // ensure cleaning up internal resources, inside the finally block.
+ if (!LeaveOpen && disposing && (_stream != null))
+ {
+ _stream.Close();
+ }
+ }
+ finally
+ {
+ if (!LeaveOpen && (_stream != null))
+ {
+ _stream = null;
+ _encoding = null;
+ _decoder = null;
+ _byteBuffer = null;
+ _charBuffer = null;
+ _charPos = 0;
+ _charLen = 0;
+ base.Dispose(disposing);
+ }
+ }
+ }
+
+ public virtual Encoding CurrentEncoding
+ {
+ get { return _encoding; }
+ }
+
+ public virtual Stream BaseStream
+ {
+ get { return _stream; }
+ }
+
+ internal bool LeaveOpen
+ {
+ get { return !_closable; }
+ }
+
+ // DiscardBufferedData tells StreamReader to throw away its internal
+ // buffer contents. This is useful if the user needs to seek on the
+ // underlying stream to a known location then wants the StreamReader
+ // to start reading from this new point. This method should be called
+ // very sparingly, if ever, since it can lead to very poor performance.
+ // However, it may be the only way of handling some scenarios where
+ // users need to re-read the contents of a StreamReader a second time.
+ public void DiscardBufferedData()
+ {
+ CheckAsyncTaskInProgress();
+
+ _byteLen = 0;
+ _charLen = 0;
+ _charPos = 0;
+ // in general we'd like to have an invariant that encoding isn't null. However,
+ // for startup improvements for NullStreamReader, we want to delay load encoding.
+ if (_encoding != null)
+ {
+ _decoder = _encoding.GetDecoder();
+ }
+ _isBlocked = false;
+ }
+
+ public bool EndOfStream
+ {
+ get
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (_charPos < _charLen)
+ {
+ return false;
+ }
+
+ // This may block on pipes!
+ int numRead = ReadBuffer();
+ return numRead == 0;
+ }
+ }
+
+ public override int Peek()
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (_charPos == _charLen)
+ {
+ if (_isBlocked || ReadBuffer() == 0)
+ {
+ return -1;
+ }
+ }
+ return _charBuffer[_charPos];
+ }
+
+ public override int Read()
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (_charPos == _charLen)
+ {
+ if (ReadBuffer() == 0)
+ {
+ return -1;
+ }
+ }
+ int result = _charBuffer[_charPos];
+ _charPos++;
+ return result;
+ }
+
+ public override int Read(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ return ReadSpan(new Span<char>(buffer, index, count));
+ }
+
+ public override int Read(Span<char> buffer) =>
+ GetType() == typeof(StreamReader) ? ReadSpan(buffer) :
+ base.Read(buffer); // Defer to Read(char[], ...) if a derived type may have previously overridden it
+
+ private int ReadSpan(Span<char> buffer)
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ int charsRead = 0;
+ // As a perf optimization, if we had exactly one buffer's worth of
+ // data read in, let's try writing directly to the user's buffer.
+ bool readToUserBuffer = false;
+ int count = buffer.Length;
+ while (count > 0)
+ {
+ int n = _charLen - _charPos;
+ if (n == 0)
+ {
+ n = ReadBuffer(buffer.Slice(charsRead), out readToUserBuffer);
+ }
+ if (n == 0)
+ {
+ break; // We're at EOF
+ }
+ if (n > count)
+ {
+ n = count;
+ }
+ if (!readToUserBuffer)
+ {
+ new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Slice(charsRead));
+ _charPos += n;
+ }
+
+ charsRead += n;
+ count -= n;
+ // This function shouldn't block for an indefinite amount of time,
+ // or reading from a network stream won't work right. If we got
+ // fewer bytes than we requested, then we want to break right here.
+ if (_isBlocked)
+ {
+ break;
+ }
+ }
+
+ return charsRead;
+ }
+
+ public override string ReadToEnd()
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ // Call ReadBuffer, then pull data out of charBuffer.
+ StringBuilder sb = new StringBuilder(_charLen - _charPos);
+ do
+ {
+ sb.Append(_charBuffer, _charPos, _charLen - _charPos);
+ _charPos = _charLen; // Note we consumed these characters
+ ReadBuffer();
+ } while (_charLen > 0);
+ return sb.ToString();
+ }
+
+ public override int ReadBlock(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ return base.ReadBlock(buffer, index, count);
+ }
+
+ public override int ReadBlock(Span<char> buffer)
+ {
+ if (GetType() != typeof(StreamReader))
+ {
+ // Defer to Read(char[], ...) if a derived type may have previously overridden it.
+ return base.ReadBlock(buffer);
+ }
+
+ int i, n = 0;
+ do
+ {
+ i = ReadSpan(buffer.Slice(n));
+ n += i;
+ } while (i > 0 && n < buffer.Length);
+ return n;
+ }
+
+ // Trims n bytes from the front of the buffer.
+ private void CompressBuffer(int n)
+ {
+ Debug.Assert(_byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?");
+ Buffer.BlockCopy(_byteBuffer, n, _byteBuffer, 0, _byteLen - n);
+ _byteLen -= n;
+ }
+
+ private void DetectEncoding()
+ {
+ if (_byteLen < 2)
+ {
+ return;
+ }
+ _detectEncoding = false;
+ bool changedEncoding = false;
+ if (_byteBuffer[0] == 0xFE && _byteBuffer[1] == 0xFF)
+ {
+ // Big Endian Unicode
+
+ _encoding = Encoding.BigEndianUnicode;
+ CompressBuffer(2);
+ changedEncoding = true;
+ }
+
+ else if (_byteBuffer[0] == 0xFF && _byteBuffer[1] == 0xFE)
+ {
+ // Little Endian Unicode, or possibly little endian UTF32
+ if (_byteLen < 4 || _byteBuffer[2] != 0 || _byteBuffer[3] != 0)
+ {
+ _encoding = Encoding.Unicode;
+ CompressBuffer(2);
+ changedEncoding = true;
+ }
+ else
+ {
+ _encoding = Encoding.UTF32;
+ CompressBuffer(4);
+ changedEncoding = true;
+ }
+ }
+
+ else if (_byteLen >= 3 && _byteBuffer[0] == 0xEF && _byteBuffer[1] == 0xBB && _byteBuffer[2] == 0xBF)
+ {
+ // UTF-8
+ _encoding = Encoding.UTF8;
+ CompressBuffer(3);
+ changedEncoding = true;
+ }
+ else if (_byteLen >= 4 && _byteBuffer[0] == 0 && _byteBuffer[1] == 0 &&
+ _byteBuffer[2] == 0xFE && _byteBuffer[3] == 0xFF)
+ {
+ // Big Endian UTF32
+ _encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: true);
+ CompressBuffer(4);
+ changedEncoding = true;
+ }
+ else if (_byteLen == 2)
+ {
+ _detectEncoding = true;
+ }
+ // Note: in the future, if we change this algorithm significantly,
+ // we can support checking for the preamble of the given encoding.
+
+ if (changedEncoding)
+ {
+ _decoder = _encoding.GetDecoder();
+ int newMaxCharsPerBuffer = _encoding.GetMaxCharCount(_byteBuffer.Length);
+ if (newMaxCharsPerBuffer > _maxCharsPerBuffer)
+ {
+ _charBuffer = new char[newMaxCharsPerBuffer];
+ }
+ _maxCharsPerBuffer = newMaxCharsPerBuffer;
+ }
+ }
+
+ // Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
+ // and we will buffer the bytes read until the preamble is matched or we determine that
+ // there is no match. If there is no match, every byte read previously will be available
+ // for further consumption. If there is a match, we will compress the buffer for the
+ // leading preamble bytes
+ private bool IsPreamble()
+ {
+ if (!_checkPreamble)
+ {
+ return _checkPreamble;
+ }
+
+ ReadOnlySpan<byte> preamble = _encoding.Preamble;
+
+ Debug.Assert(_bytePos <= preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?");
+ int len = (_byteLen >= (preamble.Length)) ? (preamble.Length - _bytePos) : (_byteLen - _bytePos);
+
+ for (int i = 0; i < len; i++, _bytePos++)
+ {
+ if (_byteBuffer[_bytePos] != preamble[_bytePos])
+ {
+ _bytePos = 0;
+ _checkPreamble = false;
+ break;
+ }
+ }
+
+ Debug.Assert(_bytePos <= preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+
+ if (_checkPreamble)
+ {
+ if (_bytePos == preamble.Length)
+ {
+ // We have a match
+ CompressBuffer(preamble.Length);
+ _bytePos = 0;
+ _checkPreamble = false;
+ _detectEncoding = false;
+ }
+ }
+
+ return _checkPreamble;
+ }
+
+ internal virtual int ReadBuffer()
+ {
+ _charLen = 0;
+ _charPos = 0;
+
+ if (!_checkPreamble)
+ {
+ _byteLen = 0;
+ }
+
+ do
+ {
+ if (_checkPreamble)
+ {
+ Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+ int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
+ Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (len == 0)
+ {
+ // EOF but we might have buffered bytes from previous
+ // attempt to detect preamble that needs to be decoded now
+ if (_byteLen > 0)
+ {
+ _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
+ // Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
+ _bytePos = _byteLen = 0;
+ }
+
+ return _charLen;
+ }
+
+ _byteLen += len;
+ }
+ else
+ {
+ Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
+ _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
+ Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (_byteLen == 0) // We're at EOF
+ {
+ return _charLen;
+ }
+ }
+
+ // _isBlocked == whether we read fewer bytes than we asked for.
+ // Note we must check it here because CompressBuffer or
+ // DetectEncoding will change byteLen.
+ _isBlocked = (_byteLen < _byteBuffer.Length);
+
+ // Check for preamble before detect encoding. This is not to override the
+ // user supplied Encoding for the one we implicitly detect. The user could
+ // customize the encoding which we will loose, such as ThrowOnError on UTF8
+ if (IsPreamble())
+ {
+ continue;
+ }
+
+ // If we're supposed to detect the encoding and haven't done so yet,
+ // do it. Note this may need to be called more than once.
+ if (_detectEncoding && _byteLen >= 2)
+ {
+ DetectEncoding();
+ }
+
+ _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
+ } while (_charLen == 0);
+ //Console.WriteLine("ReadBuffer called. chars: "+charLen);
+ return _charLen;
+ }
+
+
+ // This version has a perf optimization to decode data DIRECTLY into the
+ // user's buffer, bypassing StreamReader's own buffer.
+ // This gives a > 20% perf improvement for our encodings across the board,
+ // but only when asking for at least the number of characters that one
+ // buffer's worth of bytes could produce.
+ // This optimization, if run, will break SwitchEncoding, so we must not do
+ // this on the first call to ReadBuffer.
+ private int ReadBuffer(Span<char> userBuffer, out bool readToUserBuffer)
+ {
+ _charLen = 0;
+ _charPos = 0;
+
+ if (!_checkPreamble)
+ {
+ _byteLen = 0;
+ }
+
+ int charsRead = 0;
+
+ // As a perf optimization, we can decode characters DIRECTLY into a
+ // user's char[]. We absolutely must not write more characters
+ // into the user's buffer than they asked for. Calculating
+ // encoding.GetMaxCharCount(byteLen) each time is potentially very
+ // expensive - instead, cache the number of chars a full buffer's
+ // worth of data may produce. Yes, this makes the perf optimization
+ // less aggressive, in that all reads that asked for fewer than AND
+ // returned fewer than _maxCharsPerBuffer chars won't get the user
+ // buffer optimization. This affects reads where the end of the
+ // Stream comes in the middle somewhere, and when you ask for
+ // fewer chars than your buffer could produce.
+ readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
+
+ do
+ {
+ Debug.Assert(charsRead == 0);
+
+ if (_checkPreamble)
+ {
+ Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+ int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
+ Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (len == 0)
+ {
+ // EOF but we might have buffered bytes from previous
+ // attempt to detect preamble that needs to be decoded now
+ if (_byteLen > 0)
+ {
+ if (readToUserBuffer)
+ {
+ charsRead = _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush: false);
+ _charLen = 0; // StreamReader's buffer is empty.
+ }
+ else
+ {
+ charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
+ _charLen += charsRead; // Number of chars in StreamReader's buffer.
+ }
+ }
+
+ return charsRead;
+ }
+
+ _byteLen += len;
+ }
+ else
+ {
+ Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
+
+ _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
+
+ Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (_byteLen == 0) // EOF
+ {
+ break;
+ }
+ }
+
+ // _isBlocked == whether we read fewer bytes than we asked for.
+ // Note we must check it here because CompressBuffer or
+ // DetectEncoding will change byteLen.
+ _isBlocked = (_byteLen < _byteBuffer.Length);
+
+ // Check for preamble before detect encoding. This is not to override the
+ // user supplied Encoding for the one we implicitly detect. The user could
+ // customize the encoding which we will loose, such as ThrowOnError on UTF8
+ // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
+ // doesn't change the encoding or affect _maxCharsPerBuffer
+ if (IsPreamble())
+ {
+ continue;
+ }
+
+ // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
+ if (_detectEncoding && _byteLen >= 2)
+ {
+ DetectEncoding();
+ // DetectEncoding changes some buffer state. Recompute this.
+ readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
+ }
+
+ _charPos = 0;
+ if (readToUserBuffer)
+ {
+ charsRead += _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush:false);
+ _charLen = 0; // StreamReader's buffer is empty.
+ }
+ else
+ {
+ charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
+ _charLen += charsRead; // Number of chars in StreamReader's buffer.
+ }
+ } while (charsRead == 0);
+
+ _isBlocked &= charsRead < userBuffer.Length;
+
+ //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+" readToUserBuffer: "+readToUserBuffer);
+ return charsRead;
+ }
+
+
+ // Reads a line. A line is defined as a sequence of characters followed by
+ // a carriage return ('\r'), a line feed ('\n'), or a carriage return
+ // immediately followed by a line feed. The resulting string does not
+ // contain the terminating carriage return and/or line feed. The returned
+ // value is null if the end of the input stream has been reached.
+ //
+ public override string ReadLine()
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (_charPos == _charLen)
+ {
+ if (ReadBuffer() == 0)
+ {
+ return null;
+ }
+ }
+
+ StringBuilder sb = null;
+ do
+ {
+ int i = _charPos;
+ do
+ {
+ char ch = _charBuffer[i];
+ // Note the following common line feed chars:
+ // \n - UNIX \r\n - DOS \r - Mac
+ if (ch == '\r' || ch == '\n')
+ {
+ string s;
+ if (sb != null)
+ {
+ sb.Append(_charBuffer, _charPos, i - _charPos);
+ s = sb.ToString();
+ }
+ else
+ {
+ s = new string(_charBuffer, _charPos, i - _charPos);
+ }
+ _charPos = i + 1;
+ if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
+ {
+ if (_charBuffer[_charPos] == '\n')
+ {
+ _charPos++;
+ }
+ }
+ return s;
+ }
+ i++;
+ } while (i < _charLen);
+ i = _charLen - _charPos;
+ if (sb == null)
+ {
+ sb = new StringBuilder(i + 80);
+ }
+ sb.Append(_charBuffer, _charPos, i);
+ } while (ReadBuffer() > 0);
+ return sb.ToString();
+ }
+
+ #region Task based Async APIs
+ public override Task<string> ReadLineAsync()
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Read() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Read) when we are not sure.
+ if (GetType() != typeof(StreamReader))
+ {
+ return base.ReadLineAsync();
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task<string> task = ReadLineAsyncInternal();
+ _asyncReadTask = task;
+
+ return task;
+ }
+
+ private async Task<string> ReadLineAsyncInternal()
+ {
+ if (_charPos == _charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+ {
+ return null;
+ }
+
+ StringBuilder sb = null;
+
+ do
+ {
+ char[] tmpCharBuffer = _charBuffer;
+ int tmpCharLen = _charLen;
+ int tmpCharPos = _charPos;
+ int i = tmpCharPos;
+
+ do
+ {
+ char ch = tmpCharBuffer[i];
+
+ // Note the following common line feed chars:
+ // \n - UNIX \r\n - DOS \r - Mac
+ if (ch == '\r' || ch == '\n')
+ {
+ string s;
+
+ if (sb != null)
+ {
+ sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
+ s = sb.ToString();
+ }
+ else
+ {
+ s = new string(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
+ }
+
+ _charPos = tmpCharPos = i + 1;
+
+ if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
+ {
+ tmpCharPos = _charPos;
+ if (_charBuffer[tmpCharPos] == '\n')
+ {
+ _charPos = ++tmpCharPos;
+ }
+ }
+
+ return s;
+ }
+
+ i++;
+ } while (i < tmpCharLen);
+
+ i = tmpCharLen - tmpCharPos;
+ if (sb == null)
+ {
+ sb = new StringBuilder(i + 80);
+ }
+ sb.Append(tmpCharBuffer, tmpCharPos, i);
+ } while (await ReadBufferAsync().ConfigureAwait(false) > 0);
+
+ return sb.ToString();
+ }
+
+ public override Task<string> ReadToEndAsync()
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Read() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Read) when we are not sure.
+ if (GetType() != typeof(StreamReader))
+ {
+ return base.ReadToEndAsync();
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task<string> task = ReadToEndAsyncInternal();
+ _asyncReadTask = task;
+
+ return task;
+ }
+
+ private async Task<string> ReadToEndAsyncInternal()
+ {
+ // Call ReadBuffer, then pull data out of charBuffer.
+ StringBuilder sb = new StringBuilder(_charLen - _charPos);
+ do
+ {
+ int tmpCharPos = _charPos;
+ sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos);
+ _charPos = _charLen; // We consumed these characters
+ await ReadBufferAsync().ConfigureAwait(false);
+ } while (_charLen > 0);
+
+ return sb.ToString();
+ }
+
+ public override Task<int> ReadAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Read() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Read) when we are not sure.
+ if (GetType() != typeof(StreamReader))
+ {
+ return base.ReadAsync(buffer, index, count);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task<int> task = ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
+ _asyncReadTask = task;
+
+ return task;
+ }
+
+ public override ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamReader))
+ {
+ // Ensure we use existing overrides if a class already overrode existing overloads.
+ return base.ReadAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+ }
+
+ return ReadAsyncInternal(buffer, cancellationToken);
+ }
+
+ internal override async ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
+ {
+ if (_charPos == _charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+ {
+ return 0;
+ }
+
+ int charsRead = 0;
+
+ // As a perf optimization, if we had exactly one buffer's worth of
+ // data read in, let's try writing directly to the user's buffer.
+ bool readToUserBuffer = false;
+
+ Byte[] tmpByteBuffer = _byteBuffer;
+ Stream tmpStream = _stream;
+
+ int count = buffer.Length;
+ while (count > 0)
+ {
+ // n is the characters available in _charBuffer
+ int n = _charLen - _charPos;
+
+ // charBuffer is empty, let's read from the stream
+ if (n == 0)
+ {
+ _charLen = 0;
+ _charPos = 0;
+
+ if (!_checkPreamble)
+ {
+ _byteLen = 0;
+ }
+
+ readToUserBuffer = count >= _maxCharsPerBuffer;
+
+ // We loop here so that we read in enough bytes to yield at least 1 char.
+ // We break out of the loop if the stream is blocked (EOF is reached).
+ do
+ {
+ Debug.Assert(n == 0);
+
+ if (_checkPreamble)
+ {
+ Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+ int tmpBytePos = _bytePos;
+ int len = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos), cancellationToken).ConfigureAwait(false);
+ Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (len == 0)
+ {
+ // EOF but we might have buffered bytes from previous
+ // attempts to detect preamble that needs to be decoded now
+ if (_byteLen > 0)
+ {
+ if (readToUserBuffer)
+ {
+ n = _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
+ _charLen = 0; // StreamReader's buffer is empty.
+ }
+ else
+ {
+ n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
+ _charLen += n; // Number of chars in StreamReader's buffer.
+ }
+ }
+
+ // How can part of the preamble yield any chars?
+ Debug.Assert(n == 0);
+
+ _isBlocked = true;
+ break;
+ }
+ else
+ {
+ _byteLen += len;
+ }
+ }
+ else
+ {
+ Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
+
+ _byteLen = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer), cancellationToken).ConfigureAwait(false);
+
+ Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (_byteLen == 0) // EOF
+ {
+ _isBlocked = true;
+ break;
+ }
+ }
+
+ // _isBlocked == whether we read fewer bytes than we asked for.
+ // Note we must check it here because CompressBuffer or
+ // DetectEncoding will change _byteLen.
+ _isBlocked = (_byteLen < tmpByteBuffer.Length);
+
+ // Check for preamble before detect encoding. This is not to override the
+ // user supplied Encoding for the one we implicitly detect. The user could
+ // customize the encoding which we will loose, such as ThrowOnError on UTF8
+ // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
+ // doesn't change the encoding or affect _maxCharsPerBuffer
+ if (IsPreamble())
+ {
+ continue;
+ }
+
+ // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
+ if (_detectEncoding && _byteLen >= 2)
+ {
+ DetectEncoding();
+ // DetectEncoding changes some buffer state. Recompute this.
+ readToUserBuffer = count >= _maxCharsPerBuffer;
+ }
+
+ Debug.Assert(n == 0);
+
+ _charPos = 0;
+ if (readToUserBuffer)
+ {
+ n += _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
+
+ // Why did the bytes yield no chars?
+ Debug.Assert(n > 0);
+
+ _charLen = 0; // StreamReader's buffer is empty.
+ }
+ else
+ {
+ n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
+
+ // Why did the bytes yield no chars?
+ Debug.Assert(n > 0);
+
+ _charLen += n; // Number of chars in StreamReader's buffer.
+ }
+ } while (n == 0);
+
+ if (n == 0)
+ {
+ break; // We're at EOF
+ }
+ } // if (n == 0)
+
+ // Got more chars in charBuffer than the user requested
+ if (n > count)
+ {
+ n = count;
+ }
+
+ if (!readToUserBuffer)
+ {
+ new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Span.Slice(charsRead));
+ _charPos += n;
+ }
+
+ charsRead += n;
+ count -= n;
+
+ // This function shouldn't block for an indefinite amount of time,
+ // or reading from a network stream won't work right. If we got
+ // fewer bytes than we requested, then we want to break right here.
+ if (_isBlocked)
+ {
+ break;
+ }
+ } // while (count > 0)
+
+ return charsRead;
+ }
+
+ public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Read() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Read) when we are not sure.
+ if (GetType() != typeof(StreamReader))
+ {
+ return base.ReadBlockAsync(buffer, index, count);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task<int> task = base.ReadBlockAsync(buffer, index, count);
+ _asyncReadTask = task;
+
+ return task;
+ }
+
+ public override ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamReader))
+ {
+ // If a derived type may have overridden ReadBlockAsync(char[], ...) before this overload
+ // was introduced, defer to it.
+ return base.ReadBlockAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+ }
+
+ ValueTask<int> vt = ReadBlockAsyncInternal(buffer, cancellationToken);
+ if (vt.IsCompletedSuccessfully)
+ {
+ return vt;
+ }
+
+ Task<int> t = vt.AsTask();
+ _asyncReadTask = t;
+ return new ValueTask<int>(t);
+ }
+
+ private async Task<int> ReadBufferAsync()
+ {
+ _charLen = 0;
+ _charPos = 0;
+ Byte[] tmpByteBuffer = _byteBuffer;
+ Stream tmpStream = _stream;
+
+ if (!_checkPreamble)
+ {
+ _byteLen = 0;
+ }
+ do
+ {
+ if (_checkPreamble)
+ {
+ Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+ int tmpBytePos = _bytePos;
+ int len = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos)).ConfigureAwait(false);
+ Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
+
+ if (len == 0)
+ {
+ // EOF but we might have buffered bytes from previous
+ // attempt to detect preamble that needs to be decoded now
+ if (_byteLen > 0)
+ {
+ _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
+ // Need to zero out the _byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
+ _bytePos = 0; _byteLen = 0;
+ }
+
+ return _charLen;
+ }
+
+ _byteLen += len;
+ }
+ else
+ {
+ Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
+ _byteLen = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer)).ConfigureAwait(false);
+ Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! Bug in stream class.");
+
+ if (_byteLen == 0) // We're at EOF
+ {
+ return _charLen;
+ }
+ }
+
+ // _isBlocked == whether we read fewer bytes than we asked for.
+ // Note we must check it here because CompressBuffer or
+ // DetectEncoding will change _byteLen.
+ _isBlocked = (_byteLen < tmpByteBuffer.Length);
+
+ // Check for preamble before detect encoding. This is not to override the
+ // user supplied Encoding for the one we implicitly detect. The user could
+ // customize the encoding which we will loose, such as ThrowOnError on UTF8
+ if (IsPreamble())
+ {
+ continue;
+ }
+
+ // If we're supposed to detect the encoding and haven't done so yet,
+ // do it. Note this may need to be called more than once.
+ if (_detectEncoding && _byteLen >= 2)
+ {
+ DetectEncoding();
+ }
+
+ _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
+ } while (_charLen == 0);
+
+ return _charLen;
+ }
+#endregion
+
+
+ // No data, class doesn't need to be serializable.
+ // Note this class is threadsafe.
+ private class NullStreamReader : StreamReader
+ {
+ // Instantiating Encoding causes unnecessary perf hit.
+ internal NullStreamReader()
+ {
+ Init(Stream.Null);
+ }
+
+ public override Stream BaseStream
+ {
+ get { return Stream.Null; }
+ }
+
+ public override Encoding CurrentEncoding
+ {
+ get { return Encoding.Unicode; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // Do nothing - this is essentially unclosable.
+ }
+
+ public override int Peek()
+ {
+ return -1;
+ }
+
+ public override int Read()
+ {
+ return -1;
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int Read(char[] buffer, int index, int count)
+ {
+ return 0;
+ }
+
+ public override string ReadLine()
+ {
+ return null;
+ }
+
+ public override string ReadToEnd()
+ {
+ return string.Empty;
+ }
+
+ internal override int ReadBuffer()
+ {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
new file mode 100644
index 000000000..a37624428
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
@@ -0,0 +1,986 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO
+{
+ // This class implements a TextWriter for writing characters to a Stream.
+ // This is designed for character output in a particular Encoding,
+ // whereas the Stream class is designed for byte input and output.
+ public class StreamWriter : TextWriter
+ {
+ // For UTF-8, the values of 1K for the default buffer size and 4K for the
+ // file stream buffer size are reasonable & give very reasonable
+ // performance for in terms of construction time for the StreamWriter and
+ // write perf. Note that for UTF-8, we end up allocating a 4K byte buffer,
+ // which means we take advantage of adaptive buffering code.
+ // The performance using UnicodeEncoding is acceptable.
+ private const int DefaultBufferSize = 1024; // char[]
+ private const int DefaultFileStreamBufferSize = 4096;
+ private const int MinBufferSize = 128;
+
+ private const int DontCopyOnWriteLineThreshold = 512;
+
+ // Bit bucket - Null has no backing store. Non closable.
+ public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, true);
+
+ private Stream _stream;
+ private Encoding _encoding;
+ private Encoder _encoder;
+ private byte[] _byteBuffer;
+ private char[] _charBuffer;
+ private int _charPos;
+ private int _charLen;
+ private bool _autoFlush;
+ private bool _haveWrittenPreamble;
+ private bool _closable;
+
+ // We don't guarantee thread safety on StreamWriter, but we should at
+ // least prevent users from trying to write anything while an Async
+ // write from the same thread is in progress.
+ private volatile Task _asyncWriteTask;
+
+ private void CheckAsyncTaskInProgress()
+ {
+ // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety.
+ // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
+
+ Task t = _asyncWriteTask;
+
+ if (t != null && !t.IsCompleted)
+ throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
+ }
+
+ // The high level goal is to be tolerant of encoding errors when we read and very strict
+ // when we write. Hence, default StreamWriter encoding will throw on encoding error.
+ // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character
+ // D800-DBFF without a following low surrogate character DC00-DFFF), it will cause the
+ // internal StreamWriter's state to be irrecoverable as it would have buffered the
+ // illegal chars and any subsequent call to Flush() would hit the encoding error again.
+ // Even Close() will hit the exception as it would try to flush the unwritten data.
+ // Maybe we can add a DiscardBufferedData() method to get out of such situation (like
+ // StreamReader though for different reason). Either way, the buffered data will be lost!
+ private static Encoding UTF8NoBOM => EncodingCache.UTF8NoBOM;
+
+
+ internal StreamWriter() : base(null)
+ { // Ask for CurrentCulture all the time
+ }
+
+ public StreamWriter(Stream stream)
+ : this(stream, UTF8NoBOM, DefaultBufferSize, false)
+ {
+ }
+
+ public StreamWriter(Stream stream, Encoding encoding)
+ : this(stream, encoding, DefaultBufferSize, false)
+ {
+ }
+
+ // Creates a new StreamWriter for the given stream. The
+ // character encoding is set by encoding and the buffer size,
+ // in number of 16-bit characters, is set by bufferSize.
+ //
+ public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
+ : this(stream, encoding, bufferSize, false)
+ {
+ }
+
+ public StreamWriter(Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)
+ : base(null) // Ask for CurrentCulture all the time
+ {
+ if (stream == null || encoding == null)
+ {
+ throw new ArgumentNullException(stream == null ? nameof(stream) : nameof(encoding));
+ }
+ if (!stream.CanWrite)
+ {
+ throw new ArgumentException(SR.Argument_StreamNotWritable);
+ }
+ if (bufferSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+ }
+
+ Init(stream, encoding, bufferSize, leaveOpen);
+ }
+
+ public StreamWriter(string path)
+ : this(path, false, UTF8NoBOM, DefaultBufferSize)
+ {
+ }
+
+ public StreamWriter(string path, bool append)
+ : this(path, append, UTF8NoBOM, DefaultBufferSize)
+ {
+ }
+
+ public StreamWriter(string path, bool append, Encoding encoding)
+ : this(path, append, encoding, DefaultBufferSize)
+ {
+ }
+
+ public StreamWriter(string path, bool append, Encoding encoding, int bufferSize)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ if (encoding == null)
+ throw new ArgumentNullException(nameof(encoding));
+ if (path.Length == 0)
+ throw new ArgumentException(SR.Argument_EmptyPath);
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+
+ Stream stream = new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read,
+ DefaultFileStreamBufferSize, FileOptions.SequentialScan);
+ Init(stream, encoding, bufferSize, shouldLeaveOpen: false);
+ }
+
+ private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen)
+ {
+ _stream = streamArg;
+ _encoding = encodingArg;
+ _encoder = _encoding.GetEncoder();
+ if (bufferSize < MinBufferSize)
+ {
+ bufferSize = MinBufferSize;
+ }
+
+ _charBuffer = new char[bufferSize];
+ _byteBuffer = new byte[_encoding.GetMaxByteCount(bufferSize)];
+ _charLen = bufferSize;
+ // If we're appending to a Stream that already has data, don't write
+ // the preamble.
+ if (_stream.CanSeek && _stream.Position > 0)
+ {
+ _haveWrittenPreamble = true;
+ }
+
+ _closable = !shouldLeaveOpen;
+ }
+
+ public override void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ // We need to flush any buffered data if we are being closed/disposed.
+ // Also, we never close the handles for stdout & friends. So we can safely
+ // write any buffered data to those streams even during finalization, which
+ // is generally the right thing to do.
+ if (_stream != null)
+ {
+ // Note: flush on the underlying stream can throw (ex., low disk space)
+ if (disposing /* || (LeaveOpen && stream is __ConsoleStream) */)
+ {
+ CheckAsyncTaskInProgress();
+
+ Flush(true, true);
+ }
+ }
+ }
+ finally
+ {
+ // Dispose of our resources if this StreamWriter is closable.
+ // Note: Console.Out and other such non closable streamwriters should be left alone
+ if (!LeaveOpen && _stream != null)
+ {
+ try
+ {
+ // Attempt to close the stream even if there was an IO error from Flushing.
+ // Note that Stream.Close() can potentially throw here (may or may not be
+ // due to the same Flush error). In this case, we still need to ensure
+ // cleaning up internal resources, hence the finally block.
+ if (disposing)
+ {
+ _stream.Close();
+ }
+ }
+ finally
+ {
+ _stream = null;
+ _byteBuffer = null;
+ _charBuffer = null;
+ _encoding = null;
+ _encoder = null;
+ _charLen = 0;
+ base.Dispose(disposing);
+ }
+ }
+ }
+ }
+
+ public override void Flush()
+ {
+ CheckAsyncTaskInProgress();
+
+ Flush(true, true);
+ }
+
+ private void Flush(bool flushStream, bool flushEncoder)
+ {
+ // flushEncoder should be true at the end of the file and if
+ // the user explicitly calls Flush (though not if AutoFlush is true).
+ // This is required to flush any dangling characters from our UTF-7
+ // and UTF-8 encoders.
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ // Perf boost for Flush on non-dirty writers.
+ if (_charPos == 0 && !flushStream && !flushEncoder)
+ {
+ return;
+ }
+
+ if (!_haveWrittenPreamble)
+ {
+ _haveWrittenPreamble = true;
+ ReadOnlySpan<byte> preamble = _encoding.Preamble;
+ if (preamble.Length > 0)
+ {
+ _stream.Write(preamble);
+ }
+ }
+
+ int count = _encoder.GetBytes(_charBuffer, 0, _charPos, _byteBuffer, 0, flushEncoder);
+ _charPos = 0;
+ if (count > 0)
+ {
+ _stream.Write(_byteBuffer, 0, count);
+ }
+ // By definition, calling Flush should flush the stream, but this is
+ // only necessary if we passed in true for flushStream. The Web
+ // Services guys have some perf tests where flushing needlessly hurts.
+ if (flushStream)
+ {
+ _stream.Flush();
+ }
+ }
+
+ public virtual bool AutoFlush
+ {
+ get { return _autoFlush; }
+
+ set
+ {
+ CheckAsyncTaskInProgress();
+
+ _autoFlush = value;
+ if (value)
+ {
+ Flush(true, false);
+ }
+ }
+ }
+
+ public virtual Stream BaseStream
+ {
+ get { return _stream; }
+ }
+
+ internal bool LeaveOpen
+ {
+ get { return !_closable; }
+ }
+
+ internal bool HaveWrittenPreamble
+ {
+ set { _haveWrittenPreamble = value; }
+ }
+
+ public override Encoding Encoding
+ {
+ get { return _encoding; }
+ }
+
+ public override void Write(char value)
+ {
+ CheckAsyncTaskInProgress();
+
+ if (_charPos == _charLen)
+ {
+ Flush(false, false);
+ }
+
+ _charBuffer[_charPos] = value;
+ _charPos++;
+ if (_autoFlush)
+ {
+ Flush(true, false);
+ }
+ }
+
+ public override void Write(char[] buffer)
+ {
+ if (buffer != null)
+ {
+ WriteCore(buffer, _autoFlush);
+ }
+ }
+
+ public override void Write(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ WriteCore(new ReadOnlySpan<char>(buffer, index, count), _autoFlush);
+ }
+
+ public override void Write(ReadOnlySpan<char> buffer)
+ {
+ if (GetType() == typeof(StreamWriter))
+ {
+ WriteCore(buffer, _autoFlush);
+ }
+ else
+ {
+ // If a derived class may have overridden existing Write behavior,
+ // we need to make sure we use it.
+ base.Write(buffer);
+ }
+ }
+
+ private unsafe void WriteCore(ReadOnlySpan<char> buffer, bool autoFlush)
+ {
+ CheckAsyncTaskInProgress();
+
+ if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation
+ buffer.Length <= _charLen - _charPos)
+ {
+ // For very short buffers and when we don't need to worry about running out of space
+ // in the char buffer, just copy the chars individually.
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ _charBuffer[_charPos++] = buffer[i];
+ }
+ }
+ else
+ {
+ // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks.
+ // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are
+ // resulting in significant overhead (even when the if branch above is taken rather than this
+ // else) due to temporaries that need to be cleared. Given the use of unsafe code, we also
+ // make local copies of instance state to protect against potential concurrent misuse.
+
+ char[] charBuffer = _charBuffer;
+ if (charBuffer == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
+ fixed (char* dstPtr = &charBuffer[0])
+ {
+ char* srcPtr = bufferPtr;
+ int count = buffer.Length;
+ int dstPos = _charPos; // use a local copy of _charPos for safety
+ while (count > 0)
+ {
+ if (dstPos == charBuffer.Length)
+ {
+ Flush(false, false);
+ dstPos = 0;
+ }
+
+ int n = Math.Min(charBuffer.Length - dstPos, count);
+ int bytesToCopy = n * sizeof(char);
+
+ Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy);
+
+ _charPos += n;
+ dstPos += n;
+ srcPtr += n;
+ count -= n;
+ }
+ }
+ }
+
+ if (autoFlush)
+ {
+ Flush(true, false);
+ }
+ }
+
+ public override void Write(string value)
+ {
+ if (value != null)
+ {
+ WriteCore(value.AsSpan(), _autoFlush);
+ }
+ }
+
+ //
+ // Optimize the most commonly used WriteLine overload. This optimization is important for System.Console in particular
+ // because of it will make one WriteLine equal to one call to the OS instead of two in the common case.
+ //
+ public override void WriteLine(string value)
+ {
+ CheckAsyncTaskInProgress();
+ if (value != null)
+ {
+ WriteCore(value.AsSpan(), autoFlush: false);
+ }
+ WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
+ }
+
+ public override void WriteLine(ReadOnlySpan<char> value)
+ {
+ if (GetType() == typeof(StreamWriter))
+ {
+ CheckAsyncTaskInProgress();
+ WriteCore(value, autoFlush: false);
+ WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
+ }
+ else
+ {
+ // If a derived class may have overridden existing WriteLine behavior,
+ // we need to make sure we use it.
+ base.WriteLine(value);
+ }
+ }
+
+ #region Task based Async APIs
+ public override Task WriteAsync(char value)
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteAsync(value);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+ // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+ // to ensure performant access inside the state machine that corresponds this async method.
+ // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+ private static async Task WriteAsyncInternal(StreamWriter _this, char value,
+ char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+ bool autoFlush, bool appendNewLine)
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ charBuffer[charPos] = value;
+ charPos++;
+
+ if (appendNewLine)
+ {
+ for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ charBuffer[charPos] = coreNewLine[i];
+ charPos++;
+ }
+ }
+
+ if (autoFlush)
+ {
+ await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ _this.CharPos_Prop = charPos;
+ }
+
+ public override Task WriteAsync(string value)
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteAsync(value);
+ }
+
+ if (value != null)
+ {
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+ else
+ {
+ return Task.CompletedTask;
+ }
+ }
+
+ // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+ // to ensure performant access inside the state machine that corresponds this async method.
+ // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+ private static async Task WriteAsyncInternal(StreamWriter _this, string value,
+ char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+ bool autoFlush, bool appendNewLine)
+ {
+ Debug.Assert(value != null);
+
+ int count = value.Length;
+ int index = 0;
+
+ while (count > 0)
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ int n = charLen - charPos;
+ if (n > count)
+ {
+ n = count;
+ }
+
+ Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
+
+ value.CopyTo(index, charBuffer, charPos, n);
+
+ charPos += n;
+ index += n;
+ count -= n;
+ }
+
+ if (appendNewLine)
+ {
+ for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ charBuffer[charPos] = coreNewLine[i];
+ charPos++;
+ }
+ }
+
+ if (autoFlush)
+ {
+ await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ _this.CharPos_Prop = charPos;
+ }
+
+ public override Task WriteAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteAsync(buffer, index, count);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+ public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamWriter))
+ {
+ // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it.
+ return base.WriteAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken);
+ _asyncWriteTask = task;
+ return task;
+ }
+
+ // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+ // to ensure performant access inside the state machine that corresponds this async method.
+ // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+ private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory<char> source,
+ char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+ bool autoFlush, bool appendNewLine, CancellationToken cancellationToken)
+ {
+ int copied = 0;
+ while (copied < source.Length)
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ int n = Math.Min(charLen - charPos, source.Length - copied);
+ Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
+
+ source.Span.Slice(copied, n).CopyTo(new Span<char>(charBuffer, charPos, n));
+ charPos += n;
+ copied += n;
+ }
+
+ if (appendNewLine)
+ {
+ for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
+ {
+ if (charPos == charLen)
+ {
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ charBuffer[charPos] = coreNewLine[i];
+ charPos++;
+ }
+ }
+
+ if (autoFlush)
+ {
+ await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+ Debug.Assert(_this._charPos == 0);
+ charPos = 0;
+ }
+
+ _this.CharPos_Prop = charPos;
+ }
+
+ public override Task WriteLineAsync()
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync();
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, ReadOnlyMemory<char>.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+
+ public override Task WriteLineAsync(char value)
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync(value);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+
+ public override Task WriteLineAsync(string value)
+ {
+ if (value == null)
+ {
+ return WriteLineAsync();
+ }
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync(value);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+
+ public override Task WriteLineAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync(buffer, index, count);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+ public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+
+ public override Task FlushAsync()
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Flush() which a subclass might have overridden. To be safe
+ // we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Flush) when we are not sure.
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.FlushAsync();
+ }
+
+ // flushEncoder should be true at the end of the file and if
+ // the user explicitly calls Flush (though not if AutoFlush is true).
+ // This is required to flush any dangling characters from our UTF-7
+ // and UTF-8 encoders.
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ Task task = FlushAsyncInternal(true, true, _charBuffer, _charPos);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+ private int CharPos_Prop
+ {
+ set { _charPos = value; }
+ }
+
+ private bool HaveWrittenPreamble_Prop
+ {
+ set { _haveWrittenPreamble = value; }
+ }
+
+ private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
+ char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ // Perf boost for Flush on non-dirty writers.
+ if (sCharPos == 0 && !flushStream && !flushEncoder)
+ {
+ return Task.CompletedTask;
+ }
+
+ Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble,
+ _encoding, _encoder, _byteBuffer, _stream, cancellationToken);
+
+ _charPos = 0;
+ return flushTask;
+ }
+
+
+ // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+ // to ensure performant access inside the state machine that corresponds this async method.
+ private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
+ char[] charBuffer, int charPos, bool haveWrittenPreamble,
+ Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream, CancellationToken cancellationToken)
+ {
+ if (!haveWrittenPreamble)
+ {
+ _this.HaveWrittenPreamble_Prop = true;
+ byte[] preamble = encoding.GetPreamble();
+ if (preamble.Length > 0)
+ {
+ await stream.WriteAsync(new ReadOnlyMemory<byte>(preamble), cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
+ if (count > 0)
+ {
+ await stream.WriteAsync(new ReadOnlyMemory<byte>(byteBuffer, 0, count), cancellationToken).ConfigureAwait(false);
+ }
+
+ // By definition, calling Flush should flush the stream, but this is
+ // only necessary if we passed in true for flushStream. The Web
+ // Services guys have some perf tests where flushing needlessly hurts.
+ if (flushStream)
+ {
+ await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
+ }
+ }
+ #endregion
+ } // class StreamWriter
+} // namespace
diff --git a/src/System.Private.CoreLib/shared/System/IO/TextReader.cs b/src/System.Private.CoreLib/shared/System/IO/TextReader.cs
new file mode 100644
index 000000000..eb94dd759
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/IO/TextReader.cs
@@ -0,0 +1,412 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Buffers;
+
+namespace System.IO
+{
+ // This abstract base class represents a reader that can read a sequential
+ // stream of characters. This is not intended for reading bytes -
+ // there are methods on the Stream class to read bytes.
+ // A subclass must minimally implement the Peek() and Read() methods.
+ //
+ // This class is intended for character input, not bytes.
+ // There are methods on the Stream class for reading bytes.
+ public abstract partial class TextReader : MarshalByRefObject, IDisposable
+ {
+ public static readonly TextReader Null = new NullTextReader();
+
+ protected TextReader() { }
+
+ public virtual void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ // Returns the next available character without actually reading it from
+ // the input stream. The current position of the TextReader is not changed by
+ // this operation. The returned value is -1 if no further characters are
+ // available.
+ //
+ // This default method simply returns -1.
+ //
+ public virtual int Peek()
+ {
+ return -1;
+ }
+
+ // Reads the next character from the input stream. The returned value is
+ // -1 if no further characters are available.
+ //
+ // This default method simply returns -1.
+ //
+ public virtual int Read()
+ {
+ return -1;
+ }
+
+ // Reads a block of characters. This method will read up to
+ // count characters from this TextReader into the
+ // buffer character array starting at position
+ // index. Returns the actual number of characters read.
+ //
+ public virtual int Read(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ int n;
+ for (n = 0; n < count; n++)
+ {
+ int ch = Read();
+ if (ch == -1) break;
+ buffer[index + n] = (char)ch;
+ }
+
+ return n;
+ }
+
+ // Reads a span of characters. This method will read up to
+ // count characters from this TextReader into the
+ // span of characters Returns the actual number of characters read.
+ //
+ public virtual int Read(Span<char> buffer)
+ {
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+ try
+ {
+ int numRead = Read(array, 0, buffer.Length);
+ if ((uint)numRead > buffer.Length)
+ {
+ throw new IOException(SR.IO_InvalidReadLength);
+ }
+ new Span<char>(array, 0, numRead).CopyTo(buffer);
+ return numRead;
+ }
+ finally
+ {
+ ArrayPool<char>.Shared.Return(array);
+ }
+ }
+
+ // Reads all characters from the current position to the end of the
+ // TextReader, and returns them as one string.
+ public virtual string ReadToEnd()
+ {
+ char[] chars = new char[4096];
+ int len;
+ StringBuilder sb = new StringBuilder(4096);
+ while ((len = Read(chars, 0, chars.Length)) != 0)
+ {
+ sb.Append(chars, 0, len);
+ }
+ return sb.ToString();
+ }
+
+ // Blocking version of read. Returns only when count
+ // characters have been read or the end of the file was reached.
+ //
+ public virtual int ReadBlock(char[] buffer, int index, int count)
+ {
+ int i, n = 0;
+ do
+ {
+ n += (i = Read(buffer, index + n, count - n));
+ } while (i > 0 && n < count);
+ return n;
+ }
+
+ // Blocking version of read for span of characters. Returns only when count
+ // characters have been read or the end of the file was reached.
+ //
+ public virtual int ReadBlock(Span<char> buffer)
+ {
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+ try
+ {
+ int numRead = ReadBlock(array, 0, buffer.Length);
+ if ((uint)numRead > buffer.Length)
+ {
+ throw new IOException(SR.IO_InvalidReadLength);
+ }
+ new Span<char>(array, 0, numRead).CopyTo(buffer);
+ return numRead;
+ }
+ finally
+ {
+ ArrayPool<char>.Shared.Return(array);
+ }
+ }
+
+ // Reads a line. A line is defined as a sequence of characters followed by
+ // a carriage return ('\r'), a line feed ('\n'), or a carriage return
+ // immediately followed by a line feed. The resulting string does not
+ // contain the terminating carriage return and/or line feed. The returned
+ // value is null if the end of the input stream has been reached.
+ //
+ public virtual string ReadLine()
+ {
+ StringBuilder sb = new StringBuilder();
+ while (true)
+ {
+ int ch = Read();
+ if (ch == -1) break;
+ if (ch == '\r' || ch == '\n')
+ {
+ if (ch == '\r' && Peek() == '\n')
+ {
+ Read();
+ }
+
+ return sb.ToString();
+ }
+ sb.Append((char)ch);
+ }
+ if (sb.Length > 0)
+ {
+ return sb.ToString();
+ }
+
+ return null;
+ }
+
+ #region Task based Async APIs
+ public virtual Task<string> ReadLineAsync()
+ {
+ return Task<String>.Factory.StartNew(state =>
+ {
+ return ((TextReader)state).ReadLine();
+ },
+ this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public async virtual Task<string> ReadToEndAsync()
+ {
+ var sb = new StringBuilder(4096);
+ char[] chars = ArrayPool<char>.Shared.Rent(4096);
+ try
+ {
+ int len;
+ while ((len = await ReadAsyncInternal(chars, default).ConfigureAwait(false)) != 0)
+ {
+ sb.Append(chars, 0, len);
+ }
+ }
+ finally
+ {
+ ArrayPool<char>.Shared.Return(chars);
+ }
+ return sb.ToString();
+ }
+
+ public virtual Task<int> ReadAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ return ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
+ }
+
+ public virtual ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+ ReadAsync(array.Array, array.Offset, array.Count) :
+ Task<int>.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextReader, Memory<char>>)state;
+ return t.Item1.Read(t.Item2.Span);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+
+ internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
+ {
+ var tuple = new Tuple<TextReader, Memory<char>>(this, buffer);
+ return new ValueTask<int>(Task<int>.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextReader, Memory<char>>)state;
+ return t.Item1.Read(t.Item2.Span);
+ },
+ tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }
+
+ public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ return ReadBlockAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
+ }
+
+ public virtual ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+ ReadBlockAsync(array.Array, array.Offset, array.Count) :
+ Task<int>.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextReader, Memory<char>>)state;
+ return t.Item1.ReadBlock(t.Item2.Span);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+
+ internal async ValueTask<int> ReadBlockAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
+ {
+ int n = 0, i;
+ do
+ {
+ i = await ReadAsyncInternal(buffer.Slice(n), cancellationToken).ConfigureAwait(false);
+ n += i;
+ } while (i > 0 && n < buffer.Length);
+
+ return n;
+ }
+ #endregion
+
+ private sealed class NullTextReader : TextReader
+ {
+ public NullTextReader() { }
+
+ public override int Read(char[] buffer, int index, int count)
+ {
+ return 0;
+ }
+
+ public override string ReadLine()
+ {
+ return null;
+ }
+ }
+
+ public static TextReader Synchronized(TextReader reader)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ return reader is SyncTextReader ? reader : new SyncTextReader(reader);
+ }
+
+ internal sealed class SyncTextReader : TextReader
+ {
+ internal readonly TextReader _in;
+
+ internal SyncTextReader(TextReader t)
+ {
+ _in = t;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Close() => _in.Close();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ protected override void Dispose(bool disposing)
+ {
+ // Explicitly pick up a potentially methodimpl'ed Dispose
+ if (disposing)
+ ((IDisposable)_in).Dispose();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override int Peek() => _in.Peek();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override int Read() => _in.Read();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override int Read(char[] buffer, int index, int count) => _in.Read(buffer, index, count);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override int ReadBlock(char[] buffer, int index, int count) => _in.ReadBlock(buffer, index, count);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override string ReadLine() => _in.ReadLine();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override string ReadToEnd() => _in.ReadToEnd();
+
+ //
+ // On SyncTextReader all APIs should run synchronously, even the async ones.
+ //
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task<string> ReadLineAsync() => Task.FromResult(ReadLine());
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task<string> ReadToEndAsync() => Task.FromResult(ReadToEnd());
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (buffer.Length - index < count)
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+
+ return Task.FromResult(ReadBlock(buffer, index, count));
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task<int> ReadAsync(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (buffer.Length - index < count)
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+
+ return Task.FromResult(Read(buffer, index, count));
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs b/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs
new file mode 100644
index 000000000..48e702be6
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs
@@ -0,0 +1,870 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+using System.Threading;
+using System.Globalization;
+using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Buffers;
+
+namespace System.IO
+{
+ // This abstract base class represents a writer that can write a sequential
+ // stream of characters. A subclass must minimally implement the
+ // Write(char) method.
+ //
+ // This class is intended for character output, not bytes.
+ // There are methods on the Stream class for writing bytes.
+ public abstract partial class TextWriter : MarshalByRefObject, IDisposable
+ {
+ public static readonly TextWriter Null = new NullTextWriter();
+
+ // We don't want to allocate on every TextWriter creation, so cache the char array.
+ private static readonly char[] s_coreNewLine = Environment.NewLine.ToCharArray();
+
+ /// <summary>
+ /// This is the 'NewLine' property expressed as a char[].
+ /// It is exposed to subclasses as a protected field for read-only
+ /// purposes. You should only modify it by using the 'NewLine' property.
+ /// In particular you should never modify the elements of the array
+ /// as they are shared among many instances of TextWriter.
+ /// </summary>
+ protected char[] CoreNewLine = s_coreNewLine;
+ private string CoreNewLineStr = Environment.NewLine;
+
+ // Can be null - if so, ask for the Thread's CurrentCulture every time.
+ private IFormatProvider _internalFormatProvider;
+
+ protected TextWriter()
+ {
+ _internalFormatProvider = null; // Ask for CurrentCulture all the time.
+ }
+
+ protected TextWriter(IFormatProvider formatProvider)
+ {
+ _internalFormatProvider = formatProvider;
+ }
+
+ public virtual IFormatProvider FormatProvider
+ {
+ get
+ {
+ if (_internalFormatProvider == null)
+ {
+ return CultureInfo.CurrentCulture;
+ }
+ else
+ {
+ return _internalFormatProvider;
+ }
+ }
+ }
+
+ public virtual void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // Clears all buffers for this TextWriter and causes any buffered data to be
+ // written to the underlying device. This default method is empty, but
+ // descendant classes can override the method to provide the appropriate
+ // functionality.
+ public virtual void Flush()
+ {
+ }
+
+ public abstract Encoding Encoding
+ {
+ get;
+ }
+
+ /// <summary>
+ /// Returns the line terminator string used by this TextWriter. The default line
+ /// terminator string is Environment.NewLine, which is platform specific.
+ /// On Windows this is a carriage return followed by a line feed ("\r\n").
+ /// On OSX and Linux this is a line feed ("\n").
+ /// </summary>
+ /// <remarks>
+ /// The line terminator string is written to the text stream whenever one of the
+ /// WriteLine methods are called. In order for text written by
+ /// the TextWriter to be readable by a TextReader, only one of the following line
+ /// terminator strings should be used: "\r", "\n", or "\r\n".
+ /// </remarks>
+ public virtual string NewLine
+ {
+ get { return CoreNewLineStr; }
+ set
+ {
+ if (value == null)
+ {
+ value = Environment.NewLine;
+ }
+
+ CoreNewLineStr = value;
+ CoreNewLine = value.ToCharArray();
+ }
+ }
+
+
+ // Writes a character to the text stream. This default method is empty,
+ // but descendant classes can override the method to provide the
+ // appropriate functionality.
+ //
+ public virtual void Write(char value)
+ {
+ }
+
+ // Writes a character array to the text stream. This default method calls
+ // Write(char) for each of the characters in the character array.
+ // If the character array is null, nothing is written.
+ //
+ public virtual void Write(char[] buffer)
+ {
+ if (buffer != null)
+ {
+ Write(buffer, 0, buffer.Length);
+ }
+ }
+
+ // Writes a range of a character array to the text stream. This method will
+ // write count characters of data into this TextWriter from the
+ // buffer character array starting at position index.
+ //
+ public virtual void Write(char[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ }
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+ }
+
+ for (int i = 0; i < count; i++) Write(buffer[index + i]);
+ }
+
+ // Writes a span of characters to the text stream.
+ //
+ public virtual void Write(ReadOnlySpan<char> buffer)
+ {
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+ try
+ {
+ buffer.CopyTo(new Span<char>(array));
+ Write(array, 0, buffer.Length);
+ }
+ finally
+ {
+ ArrayPool<char>.Shared.Return(array);
+ }
+ }
+
+ // Writes the text representation of a boolean to the text stream. This
+ // method outputs either Boolean.TrueString or Boolean.FalseString.
+ //
+ public virtual void Write(bool value)
+ {
+ Write(value ? "True" : "False");
+ }
+
+ // Writes the text representation of an integer to the text stream. The
+ // text representation of the given value is produced by calling the
+ // Int32.ToString() method.
+ //
+ public virtual void Write(int value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes the text representation of an integer to the text stream. The
+ // text representation of the given value is produced by calling the
+ // UInt32.ToString() method.
+ //
+ [CLSCompliant(false)]
+ public virtual void Write(uint value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes the text representation of a long to the text stream. The
+ // text representation of the given value is produced by calling the
+ // Int64.ToString() method.
+ //
+ public virtual void Write(long value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes the text representation of an unsigned long to the text
+ // stream. The text representation of the given value is produced
+ // by calling the UInt64.ToString() method.
+ //
+ [CLSCompliant(false)]
+ public virtual void Write(ulong value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes the text representation of a float to the text stream. The
+ // text representation of the given value is produced by calling the
+ // Float.toString(float) method.
+ //
+ public virtual void Write(float value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes the text representation of a double to the text stream. The
+ // text representation of the given value is produced by calling the
+ // Double.toString(double) method.
+ //
+ public virtual void Write(double value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ public virtual void Write(decimal value)
+ {
+ Write(value.ToString(FormatProvider));
+ }
+
+ // Writes a string to the text stream. If the given string is null, nothing
+ // is written to the text stream.
+ //
+ public virtual void Write(string value)
+ {
+ if (value != null)
+ {
+ Write(value.ToCharArray());
+ }
+ }
+
+ // Writes the text representation of an object to the text stream. If the
+ // given object is null, nothing is written to the text stream.
+ // Otherwise, the object's ToString method is called to produce the
+ // string representation, and the resulting string is then written to the
+ // output stream.
+ //
+ public virtual void Write(object value)
+ {
+ if (value != null)
+ {
+ IFormattable f = value as IFormattable;
+ if (f != null)
+ {
+ Write(f.ToString(null, FormatProvider));
+ }
+ else
+ Write(value.ToString());
+ }
+ }
+
+ // Writes out a formatted string. Uses the same semantics as
+ // String.Format.
+ //
+ public virtual void Write(string format, object arg0)
+ {
+ Write(string.Format(FormatProvider, format, arg0));
+ }
+
+ // Writes out a formatted string. Uses the same semantics as
+ // String.Format.
+ //
+ public virtual void Write(string format, object arg0, object arg1)
+ {
+ Write(string.Format(FormatProvider, format, arg0, arg1));
+ }
+
+ // Writes out a formatted string. Uses the same semantics as
+ // String.Format.
+ //
+ public virtual void Write(string format, object arg0, object arg1, object arg2)
+ {
+ Write(string.Format(FormatProvider, format, arg0, arg1, arg2));
+ }
+
+ // Writes out a formatted string. Uses the same semantics as
+ // String.Format.
+ //
+ public virtual void Write(string format, params object[] arg)
+ {
+ Write(string.Format(FormatProvider, format, arg));
+ }
+
+
+ // Writes a line terminator to the text stream. The default line terminator
+ // is Environment.NewLine, but this value can be changed by setting the NewLine property.
+ //
+ public virtual void WriteLine()
+ {
+ Write(CoreNewLine);
+ }
+
+ // Writes a character followed by a line terminator to the text stream.
+ //
+ public virtual void WriteLine(char value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes an array of characters followed by a line terminator to the text
+ // stream.
+ //
+ public virtual void WriteLine(char[] buffer)
+ {
+ Write(buffer);
+ WriteLine();
+ }
+
+ // Writes a range of a character array followed by a line terminator to the
+ // text stream.
+ //
+ public virtual void WriteLine(char[] buffer, int index, int count)
+ {
+ Write(buffer, index, count);
+ WriteLine();
+ }
+
+ public virtual void WriteLine(ReadOnlySpan<char> buffer)
+ {
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+ try
+ {
+ buffer.CopyTo(new Span<char>(array));
+ WriteLine(array, 0, buffer.Length);
+ }
+ finally
+ {
+ ArrayPool<char>.Shared.Return(array);
+ }
+ }
+
+ // Writes the text representation of a boolean followed by a line
+ // terminator to the text stream.
+ //
+ public virtual void WriteLine(bool value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of an integer followed by a line
+ // terminator to the text stream.
+ //
+ public virtual void WriteLine(int value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of an unsigned integer followed by
+ // a line terminator to the text stream.
+ //
+ [CLSCompliant(false)]
+ public virtual void WriteLine(uint value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of a long followed by a line terminator
+ // to the text stream.
+ //
+ public virtual void WriteLine(long value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of an unsigned long followed by
+ // a line terminator to the text stream.
+ //
+ [CLSCompliant(false)]
+ public virtual void WriteLine(ulong value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of a float followed by a line terminator
+ // to the text stream.
+ //
+ public virtual void WriteLine(float value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes the text representation of a double followed by a line terminator
+ // to the text stream.
+ //
+ public virtual void WriteLine(double value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ public virtual void WriteLine(decimal value)
+ {
+ Write(value);
+ WriteLine();
+ }
+
+ // Writes a string followed by a line terminator to the text stream.
+ //
+ public virtual void WriteLine(string value)
+ {
+ if (value != null)
+ {
+ Write(value);
+ }
+ Write(CoreNewLineStr);
+ }
+
+ // Writes the text representation of an object followed by a line
+ // terminator to the text stream.
+ //
+ public virtual void WriteLine(object value)
+ {
+ if (value == null)
+ {
+ WriteLine();
+ }
+ else
+ {
+ // Call WriteLine(value.ToString), not Write(Object), WriteLine().
+ // This makes calls to WriteLine(Object) atomic.
+ IFormattable f = value as IFormattable;
+ if (f != null)
+ {
+ WriteLine(f.ToString(null, FormatProvider));
+ }
+ else
+ {
+ WriteLine(value.ToString());
+ }
+ }
+ }
+
+ // Writes out a formatted string and a new line. Uses the same
+ // semantics as String.Format.
+ //
+ public virtual void WriteLine(string format, object arg0)
+ {
+ WriteLine(string.Format(FormatProvider, format, arg0));
+ }
+
+ // Writes out a formatted string and a new line. Uses the same
+ // semantics as String.Format.
+ //
+ public virtual void WriteLine(string format, object arg0, object arg1)
+ {
+ WriteLine(string.Format(FormatProvider, format, arg0, arg1));
+ }
+
+ // Writes out a formatted string and a new line. Uses the same
+ // semantics as String.Format.
+ //
+ public virtual void WriteLine(string format, object arg0, object arg1, object arg2)
+ {
+ WriteLine(string.Format(FormatProvider, format, arg0, arg1, arg2));
+ }
+
+ // Writes out a formatted string and a new line. Uses the same
+ // semantics as String.Format.
+ //
+ public virtual void WriteLine(string format, params object[] arg)
+ {
+ WriteLine(string.Format(FormatProvider, format, arg));
+ }
+
+ #region Task based Async APIs
+ public virtual Task WriteAsync(char value)
+ {
+ var tuple = new Tuple<TextWriter, char>(this, value);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, char>)state;
+ t.Item1.Write(t.Item2);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public virtual Task WriteAsync(string value)
+ {
+ var tuple = new Tuple<TextWriter, string>(this, value);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, string>)state;
+ t.Item1.Write(t.Item2);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public Task WriteAsync(char[] buffer)
+ {
+ if (buffer == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ return WriteAsync(buffer, 0, buffer.Length);
+ }
+
+ public virtual Task WriteAsync(char[] buffer, int index, int count)
+ {
+ var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, char[], int, int>)state;
+ t.Item1.Write(t.Item2, t.Item3, t.Item4);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+ WriteAsync(array.Array, array.Offset, array.Count) :
+ Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
+ t.Item1.Write(t.Item2.Span);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+ public virtual Task WriteLineAsync(char value)
+ {
+ var tuple = new Tuple<TextWriter, char>(this, value);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, char>)state;
+ t.Item1.WriteLine(t.Item2);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public virtual Task WriteLineAsync(string value)
+ {
+ var tuple = new Tuple<TextWriter, string>(this, value);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, string>)state;
+ t.Item1.WriteLine(t.Item2);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public Task WriteLineAsync(char[] buffer)
+ {
+ if (buffer == null)
+ {
+ return WriteLineAsync();
+ }
+
+ return WriteLineAsync(buffer, 0, buffer.Length);
+ }
+
+ public virtual Task WriteLineAsync(char[] buffer, int index, int count)
+ {
+ var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
+ return Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, char[], int, int>)state;
+ t.Item1.WriteLine(t.Item2, t.Item3, t.Item4);
+ },
+ tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+ WriteLineAsync(array.Array, array.Offset, array.Count) :
+ Task.Factory.StartNew(state =>
+ {
+ var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
+ t.Item1.WriteLine(t.Item2.Span);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+ public virtual Task WriteLineAsync()
+ {
+ return WriteAsync(CoreNewLine);
+ }
+
+ public virtual Task FlushAsync()
+ {
+ return Task.Factory.StartNew(state =>
+ {
+ ((TextWriter)state).Flush();
+ },
+ this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+ #endregion
+
+ private sealed class NullTextWriter : TextWriter
+ {
+ internal NullTextWriter() : base(CultureInfo.InvariantCulture)
+ {
+ }
+
+ public override Encoding Encoding
+ {
+ get
+ {
+ return Encoding.Unicode;
+ }
+ }
+
+ public override void Write(char[] buffer, int index, int count)
+ {
+ }
+
+ public override void Write(string value)
+ {
+ }
+
+ // Not strictly necessary, but for perf reasons
+ public override void WriteLine()
+ {
+ }
+
+ // Not strictly necessary, but for perf reasons
+ public override void WriteLine(string value)
+ {
+ }
+
+ public override void WriteLine(object value)
+ {
+ }
+
+ public override void Write(char value)
+ {
+ }
+ }
+
+ public static TextWriter Synchronized(TextWriter writer)
+ {
+ if (writer == null)
+ throw new ArgumentNullException(nameof(writer));
+
+ return writer is SyncTextWriter ? writer : new SyncTextWriter(writer);
+ }
+
+ internal sealed class SyncTextWriter : TextWriter, IDisposable
+ {
+ private readonly TextWriter _out;
+
+ internal SyncTextWriter(TextWriter t) : base(t.FormatProvider)
+ {
+ _out = t;
+ }
+
+ public override Encoding Encoding => _out.Encoding;
+
+ public override IFormatProvider FormatProvider => _out.FormatProvider;
+
+ public override string NewLine
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ get { return _out.NewLine; }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ set { _out.NewLine = value; }
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Close() => _out.Close();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ protected override void Dispose(bool disposing)
+ {
+ // Explicitly pick up a potentially methodimpl'ed Dispose
+ if (disposing)
+ ((IDisposable)_out).Dispose();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Flush() => _out.Flush();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(char value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(char[] buffer) => _out.Write(buffer);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(char[] buffer, int index, int count) => _out.Write(buffer, index, count);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(bool value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(int value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(uint value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(long value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(ulong value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(float value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(double value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(Decimal value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(string value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(object value) => _out.Write(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(string format, object arg0) => _out.Write(format, arg0);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(string format, object arg0, object arg1) => _out.Write(format, arg0, arg1);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(string format, object arg0, object arg1, object arg2) => _out.Write(format, arg0, arg1, arg2);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void Write(string format, object[] arg) => _out.Write(format, arg);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine() => _out.WriteLine();
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(char value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(decimal value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(char[] buffer) => _out.WriteLine(buffer);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(char[] buffer, int index, int count) => _out.WriteLine(buffer, index, count);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(bool value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(int value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(uint value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(long value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(ulong value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(float value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(double value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(string value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(object value) => _out.WriteLine(value);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(string format, object arg0) => _out.WriteLine(format, arg0);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(string format, object arg0, object arg1) => _out.WriteLine(format, arg0, arg1);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(string format, object arg0, object arg1, object arg2) => _out.WriteLine(format, arg0, arg1, arg2);
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override void WriteLine(string format, object[] arg) => _out.WriteLine(format, arg);
+
+ //
+ // On SyncTextWriter all APIs should run synchronously, even the async ones.
+ //
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteAsync(char value)
+ {
+ Write(value);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteAsync(string value)
+ {
+ Write(value);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteAsync(char[] buffer, int index, int count)
+ {
+ Write(buffer, index, count);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteLineAsync(char value)
+ {
+ WriteLine(value);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteLineAsync(string value)
+ {
+ WriteLine(value);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task WriteLineAsync(char[] buffer, int index, int count)
+ {
+ WriteLine(buffer, index, count);
+ return Task.CompletedTask;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override Task FlushAsync()
+ {
+ Flush();
+ return Task.CompletedTask;
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
index 171113542..d1a13156a 100644
--- a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
@@ -510,7 +510,7 @@ namespace System.IO
// something other than an array and this is an UnmanagedMemoryStream-derived type that doesn't override Read(Span<byte>) will
// it then fall back to doing the ArrayPool/copy behavior.
return new ValueTask<int>(
- destination.TryGetArray(out ArraySegment<byte> destinationArray) ?
+ MemoryMarshal.TryGetArray(destination, out ArraySegment<byte> destinationArray) ?
Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) :
Read(destination.Span));
}
@@ -783,11 +783,11 @@ namespace System.IO
/// </summary>
/// <param name="buffer">Buffer that will be written.</param>
/// <param name="cancellationToken">Token that can be used to cancel the operation.</param>
- public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
- return Task.FromCanceled(cancellationToken);
+ return new ValueTask(Task.FromCanceled(cancellationToken));
}
try
@@ -802,11 +802,11 @@ namespace System.IO
{
Write(source.Span);
}
- return Task.CompletedTask;
+ return default;
}
catch (Exception ex)
{
- return Task.FromException(ex);
+ return new ValueTask(Task.FromException(ex));
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
index 90bb21ac5..f34c3c413 100644
--- a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
@@ -217,7 +217,7 @@ namespace System.IO
return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken);
}
- public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
{
return _unmanagedStream.WriteAsync(source, cancellationToken);
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs b/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs
index e25d3ccc3..f6dd5f3cd 100644
--- a/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs
@@ -80,7 +80,7 @@ namespace System.IO
return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path));
case Interop.Errors.ERROR_INVALID_DRIVE:
- throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path));
+ throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path));
case Interop.Errors.ERROR_INVALID_PARAMETER:
return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
diff --git a/src/System.Private.CoreLib/shared/System/Math.cs b/src/System.Private.CoreLib/shared/System/Math.cs
index bdb237da3..a175103f8 100644
--- a/src/System.Private.CoreLib/shared/System/Math.cs
+++ b/src/System.Private.CoreLib/shared/System/Math.cs
@@ -36,31 +36,73 @@ namespace System
1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15
};
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short Abs(short value)
{
- return (value >= 0) ? value : AbsHelper(value);
+ if (value < 0)
+ {
+ value = (short)-value;
+ if (value < 0)
+ {
+ ThrowAbsOverflow();
+ }
+ }
+ return value;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Abs(int value)
{
- return (value >= 0) ? value : AbsHelper(value);
+ if (value < 0)
+ {
+ value = -value;
+ if (value < 0)
+ {
+ ThrowAbsOverflow();
+ }
+ }
+ return value;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Abs(long value)
{
- return (value >= 0) ? value : AbsHelper(value);
+ if (value < 0)
+ {
+ value = -value;
+ if (value < 0)
+ {
+ ThrowAbsOverflow();
+ }
+ }
+ return value;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static sbyte Abs(sbyte value)
{
- return (value >= 0) ? value : AbsHelper(value);
+ if (value < 0)
+ {
+ value = (sbyte)-value;
+ if (value < 0)
+ {
+ ThrowAbsOverflow();
+ }
+ }
+ return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static decimal Abs(decimal value)
{
- return decimal.Abs(value);
+ return decimal.Abs(ref value);
+ }
+
+ [StackTraceHidden]
+ private static void ThrowAbsOverflow()
+ {
+ throw new OverflowException(SR.Overflow_NegateTwosCompNum);
}
public static long BigMul(int a, int b)
@@ -411,7 +453,7 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static decimal Max(decimal val1, decimal val2)
{
- return decimal.Max(val1, val2);
+ return decimal.Max(ref val1, ref val2);
}
public static double Max(double val1, double val2)
@@ -499,7 +541,7 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static decimal Min(decimal val1, decimal val2)
{
- return decimal.Min(val1, val2);
+ return decimal.Min(ref val1, ref val2);
}
public static double Min(double val1, double val2)
@@ -758,54 +800,6 @@ namespace System
return d;
}
- private static short AbsHelper(short value)
- {
- Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)");
-
- if (value == short.MinValue)
- {
- throw new OverflowException(SR.Overflow_NegateTwosCompNum);
- }
-
- return ((short)(-value));
- }
-
- private static int AbsHelper(int value)
- {
- Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)");
-
- if (value == int.MinValue)
- {
- throw new OverflowException(SR.Overflow_NegateTwosCompNum);
- }
-
- return -value;
- }
-
- private static long AbsHelper(long value)
- {
- Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)");
-
- if (value == long.MinValue)
- {
- throw new OverflowException(SR.Overflow_NegateTwosCompNum);
- }
-
- return -value;
- }
-
- private static sbyte AbsHelper(sbyte value)
- {
- Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)");
-
- if (value == sbyte.MinValue)
- {
- throw new OverflowException(SR.Overflow_NegateTwosCompNum);
- }
-
- return ((sbyte)(-value));
- }
-
private static unsafe double copysign(double x, double y)
{
var xbits = BitConverter.DoubleToInt64Bits(x);
diff --git a/src/System.Private.CoreLib/shared/System/Memory.cs b/src/System.Private.CoreLib/shared/System/Memory.cs
index 2286d699d..fca015f5e 100644
--- a/src/System.Private.CoreLib/shared/System/Memory.cs
+++ b/src/System.Private.CoreLib/shared/System/Memory.cs
@@ -8,10 +8,16 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
+#if !FEATURE_PORTABLE_SPAN
using Internal.Runtime.CompilerServices;
+#endif // FEATURE_PORTABLE_SPAN
namespace System
{
+ /// <summary>
+ /// Memory represents a contiguous region of arbitrary memory similar to <see cref="Span{T}"/>.
+ /// Unlike <see cref="Span{T}"/>, it is not a byref-like type.
+ /// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
public readonly struct Memory<T>
@@ -34,14 +40,16 @@ namespace System
/// Creates a new memory over the entirety of the target array.
/// </summary>
/// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Memory(T[] array)
{
if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ {
+ this = default;
+ return; // returns default
+ }
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
@@ -50,6 +58,26 @@ namespace System
_length = array.Length;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Memory(T[] array, int start)
+ {
+ if (array == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+ if ((uint)start > (uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _object = array;
+ _index = start;
+ _length = array.Length - start;
+ }
+
/// <summary>
/// Creates a new memory over the portion of the target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
@@ -57,8 +85,7 @@ namespace System
/// <param name="array">The target array.</param>
/// <param name="start">The index at which to begin the memory.</param>
/// <param name="length">The number of items in the memory.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
@@ -67,7 +94,12 @@ namespace System
public Memory(T[] array, int start, int length)
{
if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
@@ -77,16 +109,12 @@ namespace System
_index = start;
_length = length;
}
-
+
// Constructor for internal use only.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Memory(OwnedMemory<T> owner, int index, int length)
{
- if (owner == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
- if (index < 0 || length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
+ // No validation performed; caller must provide any necessary validation.
_object = owner;
_index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
_length = length;
@@ -105,7 +133,7 @@ namespace System
/// Defines an implicit conversion of an array to a <see cref="Memory{T}"/>
/// </summary>
public static implicit operator Memory<T>(T[] array) => new Memory<T>(array);
-
+
/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/>
/// </summary>
@@ -168,7 +196,7 @@ namespace System
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
-
+
return new Memory<T>(_object, _index + start, length);
}
@@ -191,7 +219,11 @@ namespace System
// and then cast to a Memory<T>. Such a cast can only be done with unsafe or marshaling code,
// in which case that's the dangerous operation performed by the dev, and we're just following
// suit here to make it work as best as possible.
+#if FEATURE_PORTABLE_SPAN
+ return new Span<T>(Unsafe.As<Pinnable<T>>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
+#else
return new Span<T>(ref Unsafe.As<char, T>(ref s.GetRawStringData()), s.Length).Slice(_index, _length);
+#endif // FEATURE_PORTABLE_SPAN
}
else if (_object != null)
{
@@ -227,6 +259,10 @@ namespace System
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Memory<T> destination) => Span.TryCopyTo(destination.Span);
+ /// <summary>
+ /// Returns a handle for the array.
+ /// <param name="pin">If pin is true, the GC will not move the array and hence its address can be taken</param>
+ /// </summary>
public unsafe MemoryHandle Retain(bool pin = false)
{
MemoryHandle memoryHandle = default;
@@ -234,8 +270,7 @@ namespace System
{
if (_index < 0)
{
- memoryHandle = ((OwnedMemory<T>)_object).Pin();
- memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+ memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
else if (typeof(T) == typeof(char) && _object is string s)
{
@@ -245,13 +280,21 @@ namespace System
// a readable ReadOnlyMemory<char> or a writable Memory<char> can still be pinned and
// used for interop purposes.
GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
+#if FEATURE_PORTABLE_SPAN
+ void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
memoryHandle = new MemoryHandle(null, pointer, handle);
}
else if (_object is T[] array)
{
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+#if FEATURE_PORTABLE_SPAN
+ void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
memoryHandle = new MemoryHandle(null, pointer, handle);
}
}
@@ -267,36 +310,16 @@ namespace System
}
/// <summary>
- /// Get an array segment from the underlying memory.
- /// If unable to get the array segment, return false with a default array segment.
- /// </summary>
- public bool TryGetArray(out ArraySegment<T> arraySegment)
- {
- if (_index < 0)
- {
- if (((OwnedMemory<T>)_object).TryGetArray(out var segment))
- {
- arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length);
- return true;
- }
- }
- else if (_object is T[] arr)
- {
- arraySegment = new ArraySegment<T>(arr, _index, _length);
- return true;
- }
-
- arraySegment = default(ArraySegment<T>);
- return false;
- }
-
- /// <summary>
/// Copies the contents from the memory into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
/// necessary to bridge the gap with APIs written in terms of arrays.
/// </summary>
public T[] ToArray() => Span.ToArray();
+ /// <summary>
+ /// Determines whether the specified object is equal to the current object.
+ /// Returns true if the object is Memory or ReadOnlyMemory and if both objects point to the same array and have the same length.
+ /// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj)
{
@@ -326,6 +349,9 @@ namespace System
_length == other._length;
}
+ /// <summary>
+ /// Serves as the default hash function.
+ /// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
diff --git a/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs b/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs
index 2706d0927..b1ed88199 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs
@@ -36,12 +36,16 @@ namespace System
}
if (typeof(T) == typeof(char) &&
- ((ReadOnlyMemory<char>)(object)_memory).TryGetString(out string text, out int start, out int length))
+ MemoryMarshal.TryGetString((ReadOnlyMemory<char>)(object)_memory, out string text, out int start, out int length))
{
return (T[])(object)text.Substring(start, length).ToCharArray();
}
+#if FEATURE_PORTABLE_SPAN
+ return SpanHelpers.PerTypeValues<T>.EmptyArray;
+#else
return Array.Empty<T>();
+#endif // FEATURE_PORTABLE_SPAN
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
new file mode 100644
index 000000000..56dd203e1
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
@@ -0,0 +1,516 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ /// <summary>
+ /// Extension methods for Span{T}, Memory{T}, and friends.
+ /// </summary>
+ public static partial class MemoryExtensions
+ {
+ /// <summary>
+ /// Returns a value indicating whether the specified <paramref name="value"/> occurs within the <paramref name="span"/>.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to seek within the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ return (IndexOf(span, value, comparisonType) >= 0);
+ }
+
+ /// <summary>
+ /// Determines whether this <paramref name="span"/> and the specified <paramref name="value"/> span have the same characters
+ /// when compared using the specified <paramref name="comparisonType"/> option.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to compare with the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ StringSpanHelpers.CheckStringComparison(comparisonType);
+
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None) == 0);
+
+ case StringComparison.CurrentCultureIgnoreCase:
+ return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase) == 0);
+
+ case StringComparison.InvariantCulture:
+ return (CompareInfo.Invariant.Compare(span, value, CompareOptions.None) == 0);
+
+ case StringComparison.InvariantCultureIgnoreCase:
+ return (CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase) == 0);
+
+ case StringComparison.Ordinal:
+ if (span.Length != value.Length)
+ return false;
+ if (value.Length == 0) // span.Length == value.Length == 0
+ return true;
+ return span.SequenceEqual(value); //TODO: Optimize - https://github.com/dotnet/corefx/issues/27487
+
+ case StringComparison.OrdinalIgnoreCase:
+ if (span.Length != value.Length)
+ return false;
+ if (value.Length == 0) // span.Length == value.Length == 0
+ return true;
+ return (CompareInfo.CompareOrdinalIgnoreCase(span, value) == 0);
+ }
+
+ Debug.Fail("StringComparison outside range");
+ return false;
+ }
+
+ /// <summary>
+ /// Compares the specified <paramref name="span"/> and <paramref name="value"/> using the specified <paramref name="comparisonType"/>,
+ /// and returns an integer that indicates their relative position in the sort order.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to compare with the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ StringSpanHelpers.CheckStringComparison(comparisonType);
+
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None);
+
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase);
+
+ case StringComparison.InvariantCulture:
+ return CompareInfo.Invariant.Compare(span, value, CompareOptions.None);
+
+ case StringComparison.InvariantCultureIgnoreCase:
+ return CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase);
+
+ case StringComparison.Ordinal:
+ if (span.Length == 0 || value.Length == 0)
+ return span.Length - value.Length;
+ return string.CompareOrdinal(span, value);
+
+ case StringComparison.OrdinalIgnoreCase:
+ return CompareInfo.CompareOrdinalIgnoreCase(span, value);
+ }
+
+ Debug.Fail("StringComparison outside range");
+ return 0;
+ }
+
+ /// <summary>
+ /// Reports the zero-based index of the first occurrence of the specified <paramref name="value"/> in the current <paramref name="span"/>.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to seek within the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ StringSpanHelpers.CheckStringComparison(comparisonType);
+
+ if (value.Length == 0)
+ {
+ return 0;
+ }
+
+ if (span.Length == 0)
+ {
+ return -1;
+ }
+
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return SpanHelpers.IndexOfCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.CurrentCultureIgnoreCase:
+ return SpanHelpers.IndexOfCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.InvariantCulture:
+ return SpanHelpers.IndexOfCultureHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.InvariantCultureIgnoreCase:
+ return SpanHelpers.IndexOfCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.Ordinal:
+ return SpanHelpers.IndexOfOrdinalHelper(span, value, ignoreCase: false);
+
+ case StringComparison.OrdinalIgnoreCase:
+ return SpanHelpers.IndexOfOrdinalHelper(span, value, ignoreCase: true);
+ }
+
+ Debug.Fail("StringComparison outside range");
+ return -1;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to lowercase,
+ /// using the casing rules of the specified culture.
+ /// </summary>
+ /// <param name="source">The source span.</param>
+ /// <param name="destination">The destination span which contains the transformed characters.</param>
+ /// <param name="culture">An object that supplies culture-specific casing rules.</param>
+ /// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
+ /// a temporary location before the destination is overwritten.</remarks>
+ /// <exception cref="System.ArgumentNullException">
+ /// Thrown when <paramref name="culture"/> is null.
+ /// </exception>
+ public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
+ {
+ if (culture == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
+
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ culture.TextInfo.ToLowerAsciiInvariant(source, destination);
+ else
+ culture.TextInfo.ChangeCase(source, destination, toUpper: false);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to lowercase,
+ /// using the casing rules of the invariant culture.
+ /// </summary>
+ /// <param name="source">The source span.</param>
+ /// <param name="destination">The destination span which contains the transformed characters.</param>
+ /// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
+ /// a temporary location before the destination is overwritten.</remarks>
+ public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
+ {
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ CultureInfo.InvariantCulture.TextInfo.ToLowerAsciiInvariant(source, destination);
+ else
+ CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: false);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to uppercase,
+ /// using the casing rules of the specified culture.
+ /// </summary>
+ /// <param name="source">The source span.</param>
+ /// <param name="destination">The destination span which contains the transformed characters.</param>
+ /// <param name="culture">An object that supplies culture-specific casing rules.</param>
+ /// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
+ /// a temporary location before the destination is overwritten.</remarks>
+ /// <exception cref="System.ArgumentNullException">
+ /// Thrown when <paramref name="culture"/> is null.
+ /// </exception>
+ public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
+ {
+ if (culture == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
+
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ culture.TextInfo.ToUpperAsciiInvariant(source, destination);
+ else
+ culture.TextInfo.ChangeCase(source, destination, toUpper: true);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to uppercase
+ /// using the casing rules of the invariant culture.
+ /// </summary>
+ /// <param name="source">The source span.</param>
+ /// <param name="destination">The destination span which contains the transformed characters.</param>
+ /// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
+ /// a temporary location before the destination is overwritten.</remarks>
+ public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
+ {
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ CultureInfo.InvariantCulture.TextInfo.ToUpperAsciiInvariant(source, destination);
+ else
+ CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: true);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Determines whether the end of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
+ /// </summary>
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The sequence to compare to the end of the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ if (value.Length == 0)
+ {
+ StringSpanHelpers.CheckStringComparison(comparisonType);
+ return true;
+ }
+
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return SpanHelpers.EndsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.CurrentCultureIgnoreCase:
+ return SpanHelpers.EndsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.InvariantCulture:
+ return SpanHelpers.EndsWithCultureHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.InvariantCultureIgnoreCase:
+ return SpanHelpers.EndsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.Ordinal:
+ return span.EndsWith(value); //TODO: Optimize - https://github.com/dotnet/corefx/issues/27487
+
+ case StringComparison.OrdinalIgnoreCase:
+ return SpanHelpers.EndsWithOrdinalIgnoreCaseHelper(span, value);
+
+ default:
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
+ }
+ }
+
+ /// <summary>
+ /// Determines whether the beginning of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
+ /// </summary>
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The sequence to compare to the beginning of the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ if (value.Length == 0)
+ {
+ StringSpanHelpers.CheckStringComparison(comparisonType);
+ return true;
+ }
+
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return SpanHelpers.StartsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.CurrentCultureIgnoreCase:
+ return SpanHelpers.StartsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
+
+ case StringComparison.InvariantCulture:
+ return SpanHelpers.StartsWithCultureHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.InvariantCultureIgnoreCase:
+ return SpanHelpers.StartsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
+
+ case StringComparison.Ordinal:
+ return span.StartsWith(value); //TODO: Optimize - https://github.com/dotnet/corefx/issues/27487
+
+ case StringComparison.OrdinalIgnoreCase:
+ return SpanHelpers.StartsWithOrdinalIgnoreCaseHelper(span, value);
+
+ default:
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
+ }
+ }
+
+ /// <summary>
+ /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
+ /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="T"/> contains pointers.
+ /// </exception>
+ /// <exception cref="System.OverflowException">
+ /// Thrown if the Length property of the new Span would exceed Int32.MaxValue.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<byte> AsBytes<T>(this Span<T> source)
+ where T : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+ return new Span<byte>(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
+ checked(source.Length * Unsafe.SizeOf<T>()));
+ }
+
+ /// <summary>
+ /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
+ /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="T"/> contains pointers.
+ /// </exception>
+ /// <exception cref="System.OverflowException">
+ /// Thrown if the Length property of the new Span would exceed Int32.MaxValue.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
+ where T : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+ return new ReadOnlySpan<byte>(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
+ checked(source.Length * Unsafe.SizeOf<T>()));
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array.
+ /// </summary>
+ public static Span<T> AsSpan<T>(this T[] array, int start)
+ {
+ if (array == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ return default;
+ }
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+ if ((uint)start > (uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), array.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target string.
+ /// </summary>
+ /// <param name="text">The target string.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlySpan<char> AsSpan(this string text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target string.
+ /// </summary>
+ /// <param name="text">The target string.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is null.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ public static ReadOnlySpan<char> AsSpan(this string text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target string.
+ /// </summary>
+ /// <param name="text">The target string.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ public static ReadOnlySpan<char> AsSpan(this string text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
+ /// <param name="text">The target string.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlyMemory<char> AsMemory(this string text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlyMemory<char>(text, 0, text.Length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
+ /// <param name="text">The target string.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ public static ReadOnlyMemory<char> AsMemory(this string text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlyMemory<char>(text, start, text.Length - start);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
+ /// <param name="text">The target string.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ public static ReadOnlyMemory<char> AsMemory(this string text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlyMemory<char>(text, start, length);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
new file mode 100644
index 000000000..effdecf92
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
@@ -0,0 +1,1170 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
+
+namespace System
+{
+ /// <summary>
+ /// Extension methods for Span{T}, Memory{T}, and friends.
+ /// </summary>
+ public static partial class MemoryExtensions
+ {
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
+ {
+ return span.TrimStart().TrimEnd();
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ break;
+ }
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ break;
+ }
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified character.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ //[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
+ {
+ return span.TrimStart(trimChar).TrimEnd(trimChar);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified character.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ //[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ break;
+ }
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified character.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (span[end] != trimChar)
+ break;
+ }
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ return span.TrimStart(trimChars).TrimEnd(trimChars);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[start] == trimChars[i])
+ goto Next;
+ }
+ break;
+ Next:
+ ;
+ }
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[end] == trimChars[i])
+ goto Next;
+ }
+ break;
+ Next:
+ ;
+ }
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Indicates whether the specified span contains only white-space characters.
+ /// </summary>
+ public static bool IsWhiteSpace(this ReadOnlySpan<char> span)
+ {
+ for (int i = 0; i < span.Length; i++)
+ {
+ if (!char.IsWhiteSpace(span[i]))
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The value to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOf<T>(this Span<T> span, T value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
+ return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The sequence to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The value to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOf<T>(this Span<T> span, T value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
+ return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The sequence to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
+ }
+
+ /// <summary>
+ /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool SequenceEqual<T>(this Span<T> first, ReadOnlySpan<T> second)
+ where T : IEquatable<T>
+ {
+ int length = first.Length;
+ if (typeof(T) == typeof(byte))
+ return length == second.Length &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)),
+ length);
+ return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length);
+ }
+
+ /// <summary>
+ /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
+ /// </summary>
+ public static int SequenceCompareTo<T>(this Span<T> first, ReadOnlySpan<T> second)
+ where T : IComparable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.SequenceCompareTo(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)),
+ first.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)),
+ second.Length);
+ return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length);
+ }
+
+ /// <summary>
+ /// Reverses the sequence of the elements in the entire span.
+ /// </summary>
+ public static void Reverse<T>(this Span<T> span)
+ {
+ ref T p = ref MemoryMarshal.GetReference(span);
+ int i = 0;
+ int j = span.Length - 1;
+ while (i < j)
+ {
+ T temp = Unsafe.Add(ref p, i);
+ Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j);
+ Unsafe.Add(ref p, j) = temp;
+ i++;
+ j--;
+ }
+ }
+
+ /// <summary>
+ /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The value to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOf<T>(this ReadOnlySpan<T> span, T value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
+ return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The sequence to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The value to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
+ return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value">The sequence to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this Span<T> span, T value0, T value1)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ /// <param name="value2">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="values">The set of values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
+ values.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ /// <param name="value2">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="values">The set of values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
+ values.Length);
+
+ return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+ return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ /// <param name="value2">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+ return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="values">The set of values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
+ values.Length);
+ return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+ return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="value0">One of the values to search for.</param>
+ /// <param name="value1">One of the values to search for.</param>
+ /// <param name="value2">One of the values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+ return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
+ }
+
+ /// <summary>
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// </summary>
+ /// <param name="span">The span to search.</param>
+ /// <param name="values">The set of values to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
+ where T : IEquatable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.LastIndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
+ values.Length);
+ return SpanHelpers.LastIndexOfAny<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
+ }
+
+ /// <summary>
+ /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool SequenceEqual<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second)
+ where T : IEquatable<T>
+ {
+ int length = first.Length;
+ if (typeof(T) == typeof(byte))
+ return length == second.Length &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)),
+ length);
+ return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length);
+ }
+
+ /// <summary>
+ /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SequenceCompareTo<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second)
+ where T : IComparable<T>
+ {
+ if (typeof(T) == typeof(byte))
+ return SpanHelpers.SequenceCompareTo(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)),
+ first.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)),
+ second.Length);
+ return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length);
+ }
+
+ /// <summary>
+ /// Determines whether the specified sequence appears at the start of the span.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool StartsWith<T>(this Span<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ int valueLength = value.Length;
+ if (typeof(T) == typeof(byte))
+ return valueLength <= span.Length &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ valueLength);
+ return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
+ }
+
+ /// <summary>
+ /// Determines whether the specified sequence appears at the start of the span.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool StartsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ int valueLength = value.Length;
+ if (typeof(T) == typeof(byte))
+ return valueLength <= span.Length &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ valueLength);
+ return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
+ }
+
+ /// <summary>
+ /// Determines whether the specified sequence appears at the end of the span.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool EndsWith<T>(this Span<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ int spanLength = span.Length;
+ int valueLength = value.Length;
+ if (typeof(T) == typeof(byte))
+ return valueLength <= spanLength &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ valueLength);
+ return valueLength <= spanLength &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength),
+ ref MemoryMarshal.GetReference(value),
+ valueLength);
+ }
+
+ /// <summary>
+ /// Determines whether the specified sequence appears at the end of the span.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool EndsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
+ where T : IEquatable<T>
+ {
+ int spanLength = span.Length;
+ int valueLength = value.Length;
+ if (typeof(T) == typeof(byte))
+ return valueLength <= spanLength &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ valueLength);
+ return valueLength <= spanLength &&
+ SpanHelpers.SequenceEqual(
+ ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength),
+ ref MemoryMarshal.GetReference(value),
+ valueLength);
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this T[] array)
+ {
+ return new Span<T>(array);
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array segment.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this ArraySegment<T> arraySegment)
+ {
+ return new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the entire target array.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<T> AsReadOnlySpan<T>(this T[] array)
+ {
+ return new ReadOnlySpan<T>(array);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the entire target span.
+ /// </summary>
+ public static ReadOnlySpan<T> AsReadOnlySpan<T>(this Span<T> span) => span;
+
+ /// <summary>
+ /// Creates a new readonly span over the target array segment.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<T> AsReadOnlySpan<T>(this ArraySegment<T> arraySegment)
+ {
+ return new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+ }
+
+ /// <summary>
+ /// Creates a new readonly memory over the entire target memory.
+ /// </summary>
+ public static ReadOnlyMemory<T> AsReadOnlyMemory<T>(this Memory<T> memory) => memory;
+
+ /// <summary>
+ /// Creates a new memory over the portion of the target array.
+ /// </summary>
+ public static Memory<T> AsMemory<T>(this T[] array, int start) => new Memory<T>(array, start);
+
+ /// <summary>
+ /// Copies the contents of the array into the span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ ///
+ ///<param name="array">The array to copy items from.</param>
+ /// <param name="destination">The span to copy items into.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when the destination Span is shorter than the source array.
+ /// </exception>
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyTo<T>(this T[] array, Span<T> destination)
+ {
+ new ReadOnlySpan<T>(array).CopyTo(destination);
+ }
+
+ /// <summary>
+ /// Copies the contents of the array into the memory. If the source
+ /// and destinations overlap, this method behaves as if the original values are in
+ /// a temporary location before the destination is overwritten.
+ ///
+ ///<param name="array">The array to copy items from.</param>
+ /// <param name="destination">The memory to copy items into.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when the destination is shorter than the source array.
+ /// </exception>
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyTo<T>(this T[] array, Memory<T> destination)
+ {
+ array.CopyTo(destination.Span);
+ }
+
+ //
+ // Overlaps
+ // ========
+ //
+ // The following methods can be used to determine if two sequences
+ // overlap in memory.
+ //
+ // Two sequences overlap if they have positions in common and neither
+ // is empty. Empty sequences do not overlap with any other sequence.
+ //
+ // If two sequences overlap, the element offset is the number of
+ // elements by which the second sequence is offset from the first
+ // sequence (i.e., second minus first). An exception is thrown if the
+ // number is not a whole number, which can happen when a sequence of a
+ // smaller type is cast to a sequence of a larger type with unsafe code
+ // or NonPortableCast. If the sequences do not overlap, the offset is
+ // meaningless and arbitrarily set to zero.
+ //
+ // Implementation
+ // --------------
+ //
+ // Implementing this correctly is quite tricky due of two problems:
+ //
+ // * If the sequences refer to two different objects on the managed
+ // heap, the garbage collector can move them freely around or change
+ // their relative order in memory.
+ //
+ // * The distance between two sequences can be greater than
+ // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit
+ // system).
+ //
+ // (For simplicity, the following text assumes a 32-bit system, but
+ // everything also applies to a 64-bit system if every 32 is replaced a
+ // 64.)
+ //
+ // The first problem is solved by calculating the distance with exactly
+ // one atomic operation. If the garbage collector happens to move the
+ // sequences afterwards and the sequences overlapped before, they will
+ // still overlap after the move and their distance hasn't changed. If
+ // the sequences did not overlap, the distance can change but the
+ // sequences still won't overlap.
+ //
+ // The second problem is solved by making all addresses relative to the
+ // start of the first sequence and performing all operations in
+ // unsigned integer arithmetic modulo 2³².
+ //
+ // Example
+ // -------
+ //
+ // Let's say there are two sequences, x and y. Let
+ //
+ // ref T xRef = MemoryMarshal.GetReference(x)
+ // uint xLength = x.Length * Unsafe.SizeOf<T>()
+ // ref T yRef = MemoryMarshal.GetReference(y)
+ // uint yLength = y.Length * Unsafe.SizeOf<T>()
+ //
+ // Visually, the two sequences are located somewhere in the 32-bit
+ // address space as follows:
+ //
+ // [----------------------------------------------) normal address space
+ // 0 2³²
+ // [------------------) first sequence
+ // xRef xRef + xLength
+ // [--------------------------) . second sequence
+ // yRef . yRef + yLength
+ // : . . .
+ // : . . .
+ // . . .
+ // . . .
+ // . . .
+ // [----------------------------------------------) relative address space
+ // 0 . . 2³²
+ // [------------------) : first sequence
+ // x1 . x2 :
+ // -------------) [------------- second sequence
+ // y2 y1
+ //
+ // The idea is to make all addresses relative to xRef: Let x1 be the
+ // start address of x in this relative address space, x2 the end
+ // address of x, y1 the start address of y, and y2 the end address of
+ // y:
+ //
+ // nuint x1 = 0
+ // nuint x2 = xLength
+ // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef)
+ // nuint y2 = y1 + yLength
+ //
+ // xRef relative to xRef is 0.
+ //
+ // x2 is simply x1 + xLength. This cannot overflow.
+ //
+ // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is
+ // negative, casting it to an unsigned 32-bit integer turns it into
+ // (yRef - xRef + 2³²). So, in the example above, y1 moves to the right
+ // of x2.
+ //
+ // y2 is simply y1 + yLength. Note that this can overflow, as in the
+ // example above, which must be avoided.
+ //
+ // The two sequences do *not* overlap if y is entirely in the space
+ // right of x in the relative address space. (It can't be left of it!)
+ //
+ // (y1 >= x2) && (y2 <= 2³²)
+ //
+ // Inversely, they do overlap if
+ //
+ // (y1 < x2) || (y2 > 2³²)
+ //
+ // After substituting x2 and y2 with their respective definition:
+ //
+ // == (y1 < xLength) || (y1 + yLength > 2³²)
+ //
+ // Since yLength can't be greater than the size of the address space,
+ // the overflow can be avoided as follows:
+ //
+ // == (y1 < xLength) || (y1 > 2³² - yLength)
+ //
+ // However, 2³² cannot be stored in an unsigned 32-bit integer, so one
+ // more change is needed to keep doing everything with unsigned 32-bit
+ // integers:
+ //
+ // == (y1 < xLength) || (y1 > -yLength)
+ //
+ // Due to modulo arithmetic, this gives exactly same result *except* if
+ // yLength is zero, since 2³² - 0 is 0 and not 2³². So the case
+ // y.IsEmpty must be handled separately first.
+ //
+
+ /// <summary>
+ /// Determines whether two sequences overlap in memory.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second)
+ {
+ return Overlaps((ReadOnlySpan<T>)first, second);
+ }
+
+ /// <summary>
+ /// Determines whether two sequences overlap in memory and outputs the element offset.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second, out int elementOffset)
+ {
+ return Overlaps((ReadOnlySpan<T>)first, second, out elementOffset);
+ }
+
+ /// <summary>
+ /// Determines whether two sequences overlap in memory.
+ /// </summary>
+ public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second)
+ {
+ if (first.IsEmpty || second.IsEmpty)
+ {
+ return false;
+ }
+
+ IntPtr byteOffset = Unsafe.ByteOffset(
+ ref MemoryMarshal.GetReference(first),
+ ref MemoryMarshal.GetReference(second));
+
+ if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
+ {
+ return (uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf<T>()) ||
+ (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf<T>());
+ }
+ else
+ {
+ return (ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf<T>()) ||
+ (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf<T>());
+ }
+ }
+
+ /// <summary>
+ /// Determines whether two sequences overlap in memory and outputs the element offset.
+ /// </summary>
+ public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second, out int elementOffset)
+ {
+ if (first.IsEmpty || second.IsEmpty)
+ {
+ elementOffset = 0;
+ return false;
+ }
+
+ IntPtr byteOffset = Unsafe.ByteOffset(
+ ref MemoryMarshal.GetReference(first),
+ ref MemoryMarshal.GetReference(second));
+
+ if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
+ {
+ if ((uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf<T>()) ||
+ (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf<T>()))
+ {
+ if ((int)byteOffset % Unsafe.SizeOf<T>() != 0)
+ ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
+
+ elementOffset = (int)byteOffset / Unsafe.SizeOf<T>();
+ return true;
+ }
+ else
+ {
+ elementOffset = 0;
+ return false;
+ }
+ }
+ else
+ {
+ if ((ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf<T>()) ||
+ (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf<T>()))
+ {
+ if ((long)byteOffset % Unsafe.SizeOf<T>() != 0)
+ ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
+
+ elementOffset = (int)((long)byteOffset / Unsafe.SizeOf<T>());
+ return true;
+ }
+ else
+ {
+ elementOffset = 0;
+ return false;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="Span{T}"/> for a value
+ /// using the specified <see cref="IComparable{T}"/> generic interface.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
+ /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
+ /// <returns>
+ /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparable" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T>(
+ this Span<T> span, IComparable<T> comparable)
+ {
+ return BinarySearch<T, IComparable<T>>(span, comparable);
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="Span{T}"/> for a value
+ /// using the specified <typeparamref name="TComparable"/> generic type.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
+ /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
+ /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
+ /// <returns>
+ /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparable" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T, TComparable>(
+ this Span<T> span, TComparable comparable)
+ where TComparable : IComparable<T>
+ {
+ return BinarySearch((ReadOnlySpan<T>)span, comparable);
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="Span{T}"/> for the specified <paramref name="value"/>
+ /// using the specified <typeparamref name="TComparer"/> generic type.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
+ /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
+ /// <param name="value">The object to locate. The value can be null for reference types.</param>
+ /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
+ /// /// <returns>
+ /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparer" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T, TComparer>(
+ this Span<T> span, T value, TComparer comparer)
+ where TComparer : IComparer<T>
+ {
+ return BinarySearch((ReadOnlySpan<T>)span, value, comparer);
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
+ /// using the specified <see cref="IComparable{T}"/> generic interface.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
+ /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
+ /// <returns>
+ /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparable" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T>(
+ this ReadOnlySpan<T> span, IComparable<T> comparable)
+ {
+ return BinarySearch<T, IComparable<T>>(span, comparable);
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
+ /// using the specified <typeparamref name="TComparable"/> generic type.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
+ /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
+ /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
+ /// <returns>
+ /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparable" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T, TComparable>(
+ this ReadOnlySpan<T> span, TComparable comparable)
+ where TComparable : IComparable<T>
+ {
+ return SpanHelpers.BinarySearch(span, comparable);
+ }
+
+ /// <summary>
+ /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for the specified <paramref name="value"/>
+ /// using the specified <typeparamref name="TComparer"/> generic type.
+ /// </summary>
+ /// <typeparam name="T">The element type of the span.</typeparam>
+ /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
+ /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
+ /// <param name="value">The object to locate. The value can be null for reference types.</param>
+ /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
+ /// /// <returns>
+ /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
+ /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
+ /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
+ /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name = "comparer" /> is <see langword="null"/> .
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T, TComparer>(
+ this ReadOnlySpan<T> span, T value, TComparer comparer)
+ where TComparer : IComparer<T>
+ {
+ if (comparer == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
+
+ var comparable = new SpanHelpers.ComparerComparable<T, TComparer>(
+ value, comparer);
+ return BinarySearch(span, comparable);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Number.Formatting.cs b/src/System.Private.CoreLib/shared/System/Number.Formatting.cs
index 70b35a08a..24d5db1da 100644
--- a/src/System.Private.CoreLib/shared/System/Number.Formatting.cs
+++ b/src/System.Private.CoreLib/shared/System/Number.Formatting.cs
@@ -564,7 +564,7 @@ namespace System
{
Debug.Assert(source != null);
- if (source.AsReadOnlySpan().TryCopyTo(destination))
+ if (source.AsSpan().TryCopyTo(destination))
{
charsWritten = source.Length;
return true;
@@ -1128,10 +1128,8 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe bool TryCopyTo(char* src, int length, Span<char> destination, out int charsWritten)
{
- if (length <= destination.Length)
+ if (new ReadOnlySpan<char>(src, length).TryCopyTo(destination))
{
- bool copied = new ReadOnlySpan<char>(src, length).TryCopyTo(destination);
- Debug.Assert(copied);
charsWritten = length;
return true;
}
@@ -1708,8 +1706,7 @@ namespace System
if (thousandsSepCtr >= thousandsSepPos.Length)
{
var newThousandsSepPos = new int[thousandsSepPos.Length * 2];
- bool copied = thousandsSepPos.TryCopyTo(newThousandsSepPos);
- Debug.Assert(copied, "Expect copy to succeed, as the new array is larger than the original");
+ thousandsSepPos.CopyTo(newThousandsSepPos);
thousandsSepPos = newThousandsSepPos;
}
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.cs b/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.cs
new file mode 100644
index 000000000..ea32ed380
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+namespace System.Numerics
+{
+ internal class ConstantHelper
+ {
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Byte GetByteWithAllBitsSet()
+ {
+ Byte value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Byte*)&value) = (Byte)0xff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static SByte GetSByteWithAllBitsSet()
+ {
+ SByte value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((SByte*)&value) = (SByte)0xff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static UInt16 GetUInt16WithAllBitsSet()
+ {
+ UInt16 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((UInt16*)&value) = (UInt16)0xffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Int16 GetInt16WithAllBitsSet()
+ {
+ Int16 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Int16*)&value) = (Int16)0xffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static UInt32 GetUInt32WithAllBitsSet()
+ {
+ UInt32 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((UInt32*)&value) = (UInt32)0xffffffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Int32 GetInt32WithAllBitsSet()
+ {
+ Int32 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Int32*)&value) = (Int32)0xffffffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static UInt64 GetUInt64WithAllBitsSet()
+ {
+ UInt64 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((UInt64*)&value) = (UInt64)0xffffffffffffffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Int64 GetInt64WithAllBitsSet()
+ {
+ Int64 value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Int64*)&value) = (Int64)0xffffffffffffffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Single GetSingleWithAllBitsSet()
+ {
+ Single value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Int32*)&value) = (Int32)0xffffffff;
+ }
+ }
+ return value;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Double GetDoubleWithAllBitsSet()
+ {
+ Double value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((Int64*)&value) = (Int64)0xffffffffffffffff;
+ }
+ }
+ return value;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.tt b/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.tt
new file mode 100644
index 000000000..4b1c67757
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/ConstantHelper.tt
@@ -0,0 +1,60 @@
+<#@ template debug="true" hostSpecific="true" #>
+<#@ output extension=".cs" #>
+<#@ Assembly Name="System.Core.dll" #>
+<#@ Assembly Name="System.Xml.dll" #>
+<#@ Assembly Name="System.Xml.Linq.dll" #>
+<#@ Assembly Name="System.Windows.Forms.dll" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.IO" #>
+<#@ import namespace="System.Diagnostics" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Xml.Linq" #>
+<#@ import namespace="System.Collections" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="System.Runtime.InteropServices" #>
+<#@ include file="GenerationConfig.ttinclude" #><# GenerateCopyrightHeader(); #>
+
+using System.Runtime.CompilerServices;
+
+namespace System.Numerics
+{
+ internal class ConstantHelper
+ {
+<# foreach (var type in supportedTypes)
+ {
+ string hexValue = "0x" + new string('f', Marshal.SizeOf(type) * 2);
+#>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static <#= type.Name #> Get<#= type.Name #>WithAllBitsSet()
+ {
+ <#= type.Name #> value = 0;
+ unsafe
+ {
+ unchecked
+ {
+ *((<#= GetIntegralEquivalent(type).Name #>*)&value) = (<#=GetIntegralEquivalent(type).Name#>)<#= hexValue #>;
+ }
+ }
+ return value;
+ }
+<#
+ }
+#>
+ }
+}<#+
+ public Type GetIntegralEquivalent(Type type)
+ {
+ if (type == typeof(Single))
+ {
+ return typeof(Int32);
+ }
+ else if (type == typeof(double))
+ {
+ return typeof(Int64);
+ }
+ else
+ {
+ return type;
+ }
+ }
+#> \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/GenerationConfig.ttinclude b/src/System.Private.CoreLib/shared/System/Numerics/GenerationConfig.ttinclude
new file mode 100644
index 000000000..cdd9c9521
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/GenerationConfig.ttinclude
@@ -0,0 +1,147 @@
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#+
+ /* This file includes static data used as compilation configuration for the rest of the code generation.
+ It is shared here to ensure that all generated code compiles with the same constants and configurations. */
+
+ // The set of supported numeric types to compile
+ public static Type[] supportedTypes = new[]
+ {
+ typeof(Byte), typeof(SByte), typeof(UInt16), typeof(Int16),
+ typeof(UInt32), typeof(Int32), typeof(UInt64), typeof(Int64),
+ typeof(Single), typeof(Double)
+ };
+
+ // The set of unsigned types, a subset of the above. Used for excluding from certain methods, i.e. Abs and Negate
+ public static Type[] unsignedTypes = new[]
+ {
+ typeof(Byte), typeof(UInt16), typeof(UInt32), typeof(UInt64)
+ };
+
+ public static Type[] integralTypes = new[]
+ {
+ typeof(Byte), typeof(SByte), typeof(UInt16), typeof(Int16),
+ typeof(UInt32), typeof(Int32), typeof(UInt64), typeof(Int64)
+ };
+
+ public static Type[] nonClsCompliantTypes = new[]
+ {
+ typeof(SByte), typeof(UInt16),
+ typeof(UInt32), typeof(UInt64)
+ };
+
+ // The total register size, in bytes. 16 for SSE2, 32 for AVX, 64 for AVX512
+ public static int totalSize = 16;
+
+ /* General template helper procedures */
+
+ // Returns the constructed register field name for the given type and index.
+ public string GetRegisterFieldName(Type t, int index)
+ {
+ return "register." + t.Name.ToLowerInvariant() + "_" + index;
+ }
+
+ // Returns the number of fields for a given type, based on the current configuration's register size
+ public int GetNumFields(Type t, int totalSize)
+ {
+ return totalSize / Marshal.SizeOf(t);
+ }
+
+ public static HashSet<Type> WidenableTypes { get; private set; } = new HashSet<Type>()
+ {
+ typeof(byte),
+ typeof(ushort),
+ typeof(uint),
+ typeof(sbyte),
+ typeof(short),
+ typeof(int),
+ typeof(float),
+ };
+
+ private static Dictionary<Type, Type> s_widenTargets = new Dictionary<Type, Type>()
+ {
+ { typeof(byte), typeof(ushort) },
+ { typeof(ushort), typeof(uint) },
+ { typeof(uint), typeof(ulong) },
+ { typeof(sbyte), typeof(short) },
+ { typeof(short), typeof(int) },
+ { typeof(int), typeof(long) },
+ { typeof(float), typeof(double) },
+ };
+
+ public Type GetWidenTarget(Type t)
+ {
+ Type target;
+ if (!s_widenTargets.TryGetValue(t, out target))
+ {
+ throw new InvalidOperationException("No widen target for " + t.Name);
+ }
+
+ return target;
+ }
+
+ public static HashSet<Type> NarrowableTypes { get; private set; } = new HashSet<Type>()
+ {
+ typeof(ushort),
+ typeof(uint),
+ typeof(ulong),
+ typeof(short),
+ typeof(int),
+ typeof(long),
+ typeof(double),
+ };
+
+ private static Dictionary<Type, Type> s_narrowTargets = new Dictionary<Type, Type>()
+ {
+ { typeof(ulong), typeof(uint) },
+ { typeof(uint), typeof(ushort) },
+ { typeof(ushort), typeof(byte) },
+ { typeof(long), typeof(int) },
+ { typeof(int), typeof(short) },
+ { typeof(short), typeof(sbyte) },
+ { typeof(double), typeof(float) },
+ };
+
+ public Type GetNarrowTarget(Type t)
+ {
+ Type target;
+ if (!s_narrowTargets.TryGetValue(t, out target))
+ {
+ throw new InvalidOperationException("No narrow target for " + t.Name);
+ }
+
+ return target;
+ }
+
+ public static List<KeyValuePair<Type, Type>> SameSizeConversionPairs = new List<KeyValuePair<Type, Type>>()
+ {
+ new KeyValuePair<Type, Type>(typeof(int), typeof(float)),
+ new KeyValuePair<Type, Type>(typeof(uint), typeof(float)),
+ new KeyValuePair<Type, Type>(typeof(long), typeof(double)),
+ new KeyValuePair<Type, Type>(typeof(ulong), typeof(double)),
+ new KeyValuePair<Type, Type>(typeof(float), typeof(int)),
+ new KeyValuePair<Type, Type>(typeof(float), typeof(uint)),
+ new KeyValuePair<Type, Type>(typeof(double), typeof(long)),
+ new KeyValuePair<Type, Type>(typeof(double), typeof(ulong)),
+ };
+
+ public void GenerateCopyrightHeader()
+ {
+#>// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+<#+
+ }
+
+ public string GenerateIfStatementHeader(Type type)
+ {
+ string keyword = (type == supportedTypes[0]) ? "if" : "else if";
+ return string.Format("{0} (typeof(T) == typeof({1}))", keyword, type.Name);
+ }
+
+ public string GenerateIfStatementHeader(Type type, IEnumerable<Type> allTypes)
+ {
+ string keyword = (type == allTypes.ToArray()[0]) ? "if" : "else if";
+ return string.Format("{0} (typeof(T) == typeof({1}))", keyword, type.Name);
+ }
+#> \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/Register.cs b/src/System.Private.CoreLib/shared/System/Numerics/Register.cs
new file mode 100644
index 000000000..a27e922b9
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/Register.cs
@@ -0,0 +1,172 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+namespace System.Numerics
+{
+ /// <summary>
+ /// A structure describing the layout of an SSE2-sized register.
+ /// Contains overlapping fields representing the set of valid numeric types.
+ /// Allows the generic Vector'T struct to contain an explicit field layout.
+ /// </summary>
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct Register
+ {
+ #region Internal Storage Fields
+ // Internal System.Byte Fields
+ [FieldOffset(0)]
+ internal Byte byte_0;
+ [FieldOffset(1)]
+ internal Byte byte_1;
+ [FieldOffset(2)]
+ internal Byte byte_2;
+ [FieldOffset(3)]
+ internal Byte byte_3;
+ [FieldOffset(4)]
+ internal Byte byte_4;
+ [FieldOffset(5)]
+ internal Byte byte_5;
+ [FieldOffset(6)]
+ internal Byte byte_6;
+ [FieldOffset(7)]
+ internal Byte byte_7;
+ [FieldOffset(8)]
+ internal Byte byte_8;
+ [FieldOffset(9)]
+ internal Byte byte_9;
+ [FieldOffset(10)]
+ internal Byte byte_10;
+ [FieldOffset(11)]
+ internal Byte byte_11;
+ [FieldOffset(12)]
+ internal Byte byte_12;
+ [FieldOffset(13)]
+ internal Byte byte_13;
+ [FieldOffset(14)]
+ internal Byte byte_14;
+ [FieldOffset(15)]
+ internal Byte byte_15;
+
+ // Internal System.SByte Fields
+ [FieldOffset(0)]
+ internal SByte sbyte_0;
+ [FieldOffset(1)]
+ internal SByte sbyte_1;
+ [FieldOffset(2)]
+ internal SByte sbyte_2;
+ [FieldOffset(3)]
+ internal SByte sbyte_3;
+ [FieldOffset(4)]
+ internal SByte sbyte_4;
+ [FieldOffset(5)]
+ internal SByte sbyte_5;
+ [FieldOffset(6)]
+ internal SByte sbyte_6;
+ [FieldOffset(7)]
+ internal SByte sbyte_7;
+ [FieldOffset(8)]
+ internal SByte sbyte_8;
+ [FieldOffset(9)]
+ internal SByte sbyte_9;
+ [FieldOffset(10)]
+ internal SByte sbyte_10;
+ [FieldOffset(11)]
+ internal SByte sbyte_11;
+ [FieldOffset(12)]
+ internal SByte sbyte_12;
+ [FieldOffset(13)]
+ internal SByte sbyte_13;
+ [FieldOffset(14)]
+ internal SByte sbyte_14;
+ [FieldOffset(15)]
+ internal SByte sbyte_15;
+
+ // Internal System.UInt16 Fields
+ [FieldOffset(0)]
+ internal UInt16 uint16_0;
+ [FieldOffset(2)]
+ internal UInt16 uint16_1;
+ [FieldOffset(4)]
+ internal UInt16 uint16_2;
+ [FieldOffset(6)]
+ internal UInt16 uint16_3;
+ [FieldOffset(8)]
+ internal UInt16 uint16_4;
+ [FieldOffset(10)]
+ internal UInt16 uint16_5;
+ [FieldOffset(12)]
+ internal UInt16 uint16_6;
+ [FieldOffset(14)]
+ internal UInt16 uint16_7;
+
+ // Internal System.Int16 Fields
+ [FieldOffset(0)]
+ internal Int16 int16_0;
+ [FieldOffset(2)]
+ internal Int16 int16_1;
+ [FieldOffset(4)]
+ internal Int16 int16_2;
+ [FieldOffset(6)]
+ internal Int16 int16_3;
+ [FieldOffset(8)]
+ internal Int16 int16_4;
+ [FieldOffset(10)]
+ internal Int16 int16_5;
+ [FieldOffset(12)]
+ internal Int16 int16_6;
+ [FieldOffset(14)]
+ internal Int16 int16_7;
+
+ // Internal System.UInt32 Fields
+ [FieldOffset(0)]
+ internal UInt32 uint32_0;
+ [FieldOffset(4)]
+ internal UInt32 uint32_1;
+ [FieldOffset(8)]
+ internal UInt32 uint32_2;
+ [FieldOffset(12)]
+ internal UInt32 uint32_3;
+
+ // Internal System.Int32 Fields
+ [FieldOffset(0)]
+ internal Int32 int32_0;
+ [FieldOffset(4)]
+ internal Int32 int32_1;
+ [FieldOffset(8)]
+ internal Int32 int32_2;
+ [FieldOffset(12)]
+ internal Int32 int32_3;
+
+ // Internal System.UInt64 Fields
+ [FieldOffset(0)]
+ internal UInt64 uint64_0;
+ [FieldOffset(8)]
+ internal UInt64 uint64_1;
+
+ // Internal System.Int64 Fields
+ [FieldOffset(0)]
+ internal Int64 int64_0;
+ [FieldOffset(8)]
+ internal Int64 int64_1;
+
+ // Internal System.Single Fields
+ [FieldOffset(0)]
+ internal Single single_0;
+ [FieldOffset(4)]
+ internal Single single_1;
+ [FieldOffset(8)]
+ internal Single single_2;
+ [FieldOffset(12)]
+ internal Single single_3;
+
+ // Internal System.Double Fields
+ [FieldOffset(0)]
+ internal Double double_0;
+ [FieldOffset(8)]
+ internal Double double_1;
+
+ #endregion Internal Storage Fields
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/Register.tt b/src/System.Private.CoreLib/shared/System/Numerics/Register.tt
new file mode 100644
index 000000000..a9de3b974
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/Register.tt
@@ -0,0 +1,46 @@
+<#@ template debug="true" hostSpecific="true" #>
+<#@ output extension=".cs" #>
+<#@ Assembly Name="System.Core.dll" #>
+<#@ Assembly Name="System.Xml.dll" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Runtime.InteropServices" #>
+<#@ import namespace="System.Diagnostics" #>
+<#@ include file="GenerationConfig.ttinclude" #><# GenerateCopyrightHeader(); #>
+
+using System.Runtime.InteropServices;
+
+namespace System.Numerics
+{
+ /// <summary>
+ /// A structure describing the layout of an SSE2-sized register.
+ /// Contains overlapping fields representing the set of valid numeric types.
+ /// Allows the generic Vector'T struct to contain an explicit field layout.
+ /// </summary>
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct Register
+ {
+ #region Internal Storage Fields
+<#
+ foreach (var type in supportedTypes)
+ {
+ Debug.Assert(
+ totalSize % Marshal.SizeOf(type) == 0,
+ "The size of supported structs must be a factor of the supported register size.");
+#>
+ // Internal <#= type.FullName #> Fields
+<#
+ for (int g = 0; g < totalSize / Marshal.SizeOf(type); g++)
+ {
+#>
+ [FieldOffset(<#=Marshal.SizeOf(type) * g#>)]
+ internal <#=type.Name#> <#= type.Name.ToLowerInvariant() + "_" + g #>;
+<#
+ }
+#>
+
+<#
+ }
+#> #endregion Internal Storage Fields
+ }
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/Vector.cs b/src/System.Private.CoreLib/shared/System/Numerics/Vector.cs
new file mode 100644
index 000000000..5fd286732
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/Vector.cs
@@ -0,0 +1,5503 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Globalization;
+using System.Numerics.Hashing;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace System.Numerics
+{
+ /* Note: The following patterns are used throughout the code here and are described here
+ *
+ * PATTERN:
+ * if (typeof(T) == typeof(Int32)) { ... }
+ * else if (typeof(T) == typeof(Single)) { ... }
+ * EXPLANATION:
+ * At runtime, each instantiation of Vector<T> will be type-specific, and each of these typeof blocks will be eliminated,
+ * as typeof(T) is a (JIT) compile-time constant for each instantiation. This design was chosen to eliminate any overhead from
+ * delegates and other patterns.
+ *
+ * PATTERN:
+ * if (Vector.IsHardwareAccelerated) { ... }
+ * else { ... }
+ * EXPLANATION
+ * This pattern solves two problems:
+ * 1. Allows us to unroll loops when we know the size (when no hardware acceleration is present)
+ * 2. Allows reflection to work:
+ * - If a method is called via reflection, it will not be "intrinsified", which would cause issues if we did
+ * not provide an implementation for that case (i.e. if it only included a case which assumed 16-byte registers)
+ * (NOTE: It is assumed that Vector.IsHardwareAccelerated will be a compile-time constant, eliminating these checks
+ * from the JIT'd code.)
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /// <summary>
+ /// A structure that represents a single Vector. The count of this Vector is fixed but CPU register dependent.
+ /// This struct only supports numerical types. This type is intended to be used as a building block for vectorizing
+ /// large algorithms. This type is immutable, individual elements cannot be modified.
+ /// </summary>
+ [Intrinsic]
+ public struct Vector<T> : IEquatable<Vector<T>>, IFormattable where T : struct
+ {
+ #region Fields
+ private Register register;
+ #endregion Fields
+
+ #region Static Members
+ /// <summary>
+ /// Returns the number of elements stored in the vector. This value is hardware dependent.
+ /// </summary>
+ public static int Count
+ {
+ [Intrinsic]
+ get
+ {
+ return s_count;
+ }
+ }
+ private static readonly int s_count = InitializeCount();
+
+ /// <summary>
+ /// Returns a vector containing all zeroes.
+ /// </summary>
+ public static Vector<T> Zero
+ {
+ [Intrinsic]
+ get
+ {
+ return s_zero;
+ }
+ }
+ private static readonly Vector<T> s_zero = new Vector<T>();
+
+ /// <summary>
+ /// Returns a vector containing all ones.
+ /// </summary>
+ public static Vector<T> One
+ {
+ [Intrinsic]
+ get
+ {
+ return s_one;
+ }
+ }
+ private static readonly Vector<T> s_one = new Vector<T>(GetOneValue());
+
+ internal static Vector<T> AllOnes { get { return s_allOnes; } }
+ private static readonly Vector<T> s_allOnes = new Vector<T>(GetAllBitsSetValue());
+ #endregion Static Members
+
+ #region Static Initialization
+ private struct VectorSizeHelper
+ {
+ internal Vector<T> _placeholder;
+ internal byte _byte;
+ }
+
+ // Calculates the size of this struct in bytes, by computing the offset of a field in a structure
+ private static unsafe int InitializeCount()
+ {
+ VectorSizeHelper vsh;
+ byte* vectorBase = &vsh._placeholder.register.byte_0;
+ byte* byteBase = &vsh._byte;
+ int vectorSizeInBytes = (int)(byteBase - vectorBase);
+
+ int typeSizeInBytes = -1;
+ if (typeof(T) == typeof(Byte))
+ {
+ typeSizeInBytes = sizeof(Byte);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ typeSizeInBytes = sizeof(SByte);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ typeSizeInBytes = sizeof(UInt16);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ typeSizeInBytes = sizeof(Int16);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ typeSizeInBytes = sizeof(UInt32);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ typeSizeInBytes = sizeof(Int32);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ typeSizeInBytes = sizeof(UInt64);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ typeSizeInBytes = sizeof(Int64);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ typeSizeInBytes = sizeof(Single);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ typeSizeInBytes = sizeof(Double);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+
+ return vectorSizeInBytes / typeSizeInBytes;
+ }
+ #endregion Static Initialization
+
+ #region Constructors
+ /// <summary>
+ /// Constructs a vector whose components are all <code>value</code>
+ /// </summary>
+ [Intrinsic]
+ public unsafe Vector(T value)
+ : this()
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ fixed (Byte* basePtr = &this.register.byte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Byte)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ fixed (SByte* basePtr = &this.register.sbyte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (SByte)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ fixed (UInt16* basePtr = &this.register.uint16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt16)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ fixed (Int16* basePtr = &this.register.int16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int16)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ fixed (UInt32* basePtr = &this.register.uint32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt32)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ fixed (Int32* basePtr = &this.register.int32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int32)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ fixed (UInt64* basePtr = &this.register.uint64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt64)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ fixed (Int64* basePtr = &this.register.int64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int64)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ fixed (Single* basePtr = &this.register.single_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Single)(object)value;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ fixed (Double* basePtr = &this.register.double_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Double)(object)value;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ register.byte_0 = (Byte)(object)value;
+ register.byte_1 = (Byte)(object)value;
+ register.byte_2 = (Byte)(object)value;
+ register.byte_3 = (Byte)(object)value;
+ register.byte_4 = (Byte)(object)value;
+ register.byte_5 = (Byte)(object)value;
+ register.byte_6 = (Byte)(object)value;
+ register.byte_7 = (Byte)(object)value;
+ register.byte_8 = (Byte)(object)value;
+ register.byte_9 = (Byte)(object)value;
+ register.byte_10 = (Byte)(object)value;
+ register.byte_11 = (Byte)(object)value;
+ register.byte_12 = (Byte)(object)value;
+ register.byte_13 = (Byte)(object)value;
+ register.byte_14 = (Byte)(object)value;
+ register.byte_15 = (Byte)(object)value;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ register.sbyte_0 = (SByte)(object)value;
+ register.sbyte_1 = (SByte)(object)value;
+ register.sbyte_2 = (SByte)(object)value;
+ register.sbyte_3 = (SByte)(object)value;
+ register.sbyte_4 = (SByte)(object)value;
+ register.sbyte_5 = (SByte)(object)value;
+ register.sbyte_6 = (SByte)(object)value;
+ register.sbyte_7 = (SByte)(object)value;
+ register.sbyte_8 = (SByte)(object)value;
+ register.sbyte_9 = (SByte)(object)value;
+ register.sbyte_10 = (SByte)(object)value;
+ register.sbyte_11 = (SByte)(object)value;
+ register.sbyte_12 = (SByte)(object)value;
+ register.sbyte_13 = (SByte)(object)value;
+ register.sbyte_14 = (SByte)(object)value;
+ register.sbyte_15 = (SByte)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ register.uint16_0 = (UInt16)(object)value;
+ register.uint16_1 = (UInt16)(object)value;
+ register.uint16_2 = (UInt16)(object)value;
+ register.uint16_3 = (UInt16)(object)value;
+ register.uint16_4 = (UInt16)(object)value;
+ register.uint16_5 = (UInt16)(object)value;
+ register.uint16_6 = (UInt16)(object)value;
+ register.uint16_7 = (UInt16)(object)value;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ register.int16_0 = (Int16)(object)value;
+ register.int16_1 = (Int16)(object)value;
+ register.int16_2 = (Int16)(object)value;
+ register.int16_3 = (Int16)(object)value;
+ register.int16_4 = (Int16)(object)value;
+ register.int16_5 = (Int16)(object)value;
+ register.int16_6 = (Int16)(object)value;
+ register.int16_7 = (Int16)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ register.uint32_0 = (UInt32)(object)value;
+ register.uint32_1 = (UInt32)(object)value;
+ register.uint32_2 = (UInt32)(object)value;
+ register.uint32_3 = (UInt32)(object)value;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ register.int32_0 = (Int32)(object)value;
+ register.int32_1 = (Int32)(object)value;
+ register.int32_2 = (Int32)(object)value;
+ register.int32_3 = (Int32)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ register.uint64_0 = (UInt64)(object)value;
+ register.uint64_1 = (UInt64)(object)value;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ register.int64_0 = (Int64)(object)value;
+ register.int64_1 = (Int64)(object)value;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ register.single_0 = (Single)(object)value;
+ register.single_1 = (Single)(object)value;
+ register.single_2 = (Single)(object)value;
+ register.single_3 = (Single)(object)value;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ register.double_0 = (Double)(object)value;
+ register.double_1 = (Double)(object)value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Constructs a vector from the given array. The size of the given array must be at least Vector'T.Count.
+ /// </summary>
+ [Intrinsic]
+ public unsafe Vector(T[] values) : this(values, 0) { }
+
+ /// <summary>
+ /// Constructs a vector from the given array, starting from the given index.
+ /// The array must contain at least Vector'T.Count from the given index.
+ /// </summary>
+ public unsafe Vector(T[] values, int index)
+ : this()
+ {
+ if (values == null)
+ {
+ // Match the JIT's exception type here. For perf, a NullReference is thrown instead of an ArgumentNull.
+ throw new NullReferenceException(SR.Arg_NullArgumentNullRef);
+ }
+ if (index < 0 || (values.Length - index) < Count)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ fixed (Byte* basePtr = &this.register.byte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Byte)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ fixed (SByte* basePtr = &this.register.sbyte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (SByte)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ fixed (UInt16* basePtr = &this.register.uint16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt16)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ fixed (Int16* basePtr = &this.register.int16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int16)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ fixed (UInt32* basePtr = &this.register.uint32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt32)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ fixed (Int32* basePtr = &this.register.int32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int32)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ fixed (UInt64* basePtr = &this.register.uint64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (UInt64)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ fixed (Int64* basePtr = &this.register.int64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Int64)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ fixed (Single* basePtr = &this.register.single_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Single)(object)values[g + index];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ fixed (Double* basePtr = &this.register.double_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (Double)(object)values[g + index];
+ }
+ }
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ fixed (Byte* basePtr = &this.register.byte_0)
+ {
+ *(basePtr + 0) = (Byte)(object)values[0 + index];
+ *(basePtr + 1) = (Byte)(object)values[1 + index];
+ *(basePtr + 2) = (Byte)(object)values[2 + index];
+ *(basePtr + 3) = (Byte)(object)values[3 + index];
+ *(basePtr + 4) = (Byte)(object)values[4 + index];
+ *(basePtr + 5) = (Byte)(object)values[5 + index];
+ *(basePtr + 6) = (Byte)(object)values[6 + index];
+ *(basePtr + 7) = (Byte)(object)values[7 + index];
+ *(basePtr + 8) = (Byte)(object)values[8 + index];
+ *(basePtr + 9) = (Byte)(object)values[9 + index];
+ *(basePtr + 10) = (Byte)(object)values[10 + index];
+ *(basePtr + 11) = (Byte)(object)values[11 + index];
+ *(basePtr + 12) = (Byte)(object)values[12 + index];
+ *(basePtr + 13) = (Byte)(object)values[13 + index];
+ *(basePtr + 14) = (Byte)(object)values[14 + index];
+ *(basePtr + 15) = (Byte)(object)values[15 + index];
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ fixed (SByte* basePtr = &this.register.sbyte_0)
+ {
+ *(basePtr + 0) = (SByte)(object)values[0 + index];
+ *(basePtr + 1) = (SByte)(object)values[1 + index];
+ *(basePtr + 2) = (SByte)(object)values[2 + index];
+ *(basePtr + 3) = (SByte)(object)values[3 + index];
+ *(basePtr + 4) = (SByte)(object)values[4 + index];
+ *(basePtr + 5) = (SByte)(object)values[5 + index];
+ *(basePtr + 6) = (SByte)(object)values[6 + index];
+ *(basePtr + 7) = (SByte)(object)values[7 + index];
+ *(basePtr + 8) = (SByte)(object)values[8 + index];
+ *(basePtr + 9) = (SByte)(object)values[9 + index];
+ *(basePtr + 10) = (SByte)(object)values[10 + index];
+ *(basePtr + 11) = (SByte)(object)values[11 + index];
+ *(basePtr + 12) = (SByte)(object)values[12 + index];
+ *(basePtr + 13) = (SByte)(object)values[13 + index];
+ *(basePtr + 14) = (SByte)(object)values[14 + index];
+ *(basePtr + 15) = (SByte)(object)values[15 + index];
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ fixed (UInt16* basePtr = &this.register.uint16_0)
+ {
+ *(basePtr + 0) = (UInt16)(object)values[0 + index];
+ *(basePtr + 1) = (UInt16)(object)values[1 + index];
+ *(basePtr + 2) = (UInt16)(object)values[2 + index];
+ *(basePtr + 3) = (UInt16)(object)values[3 + index];
+ *(basePtr + 4) = (UInt16)(object)values[4 + index];
+ *(basePtr + 5) = (UInt16)(object)values[5 + index];
+ *(basePtr + 6) = (UInt16)(object)values[6 + index];
+ *(basePtr + 7) = (UInt16)(object)values[7 + index];
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ fixed (Int16* basePtr = &this.register.int16_0)
+ {
+ *(basePtr + 0) = (Int16)(object)values[0 + index];
+ *(basePtr + 1) = (Int16)(object)values[1 + index];
+ *(basePtr + 2) = (Int16)(object)values[2 + index];
+ *(basePtr + 3) = (Int16)(object)values[3 + index];
+ *(basePtr + 4) = (Int16)(object)values[4 + index];
+ *(basePtr + 5) = (Int16)(object)values[5 + index];
+ *(basePtr + 6) = (Int16)(object)values[6 + index];
+ *(basePtr + 7) = (Int16)(object)values[7 + index];
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ fixed (UInt32* basePtr = &this.register.uint32_0)
+ {
+ *(basePtr + 0) = (UInt32)(object)values[0 + index];
+ *(basePtr + 1) = (UInt32)(object)values[1 + index];
+ *(basePtr + 2) = (UInt32)(object)values[2 + index];
+ *(basePtr + 3) = (UInt32)(object)values[3 + index];
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ fixed (Int32* basePtr = &this.register.int32_0)
+ {
+ *(basePtr + 0) = (Int32)(object)values[0 + index];
+ *(basePtr + 1) = (Int32)(object)values[1 + index];
+ *(basePtr + 2) = (Int32)(object)values[2 + index];
+ *(basePtr + 3) = (Int32)(object)values[3 + index];
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ fixed (UInt64* basePtr = &this.register.uint64_0)
+ {
+ *(basePtr + 0) = (UInt64)(object)values[0 + index];
+ *(basePtr + 1) = (UInt64)(object)values[1 + index];
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ fixed (Int64* basePtr = &this.register.int64_0)
+ {
+ *(basePtr + 0) = (Int64)(object)values[0 + index];
+ *(basePtr + 1) = (Int64)(object)values[1 + index];
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ fixed (Single* basePtr = &this.register.single_0)
+ {
+ *(basePtr + 0) = (Single)(object)values[0 + index];
+ *(basePtr + 1) = (Single)(object)values[1 + index];
+ *(basePtr + 2) = (Single)(object)values[2 + index];
+ *(basePtr + 3) = (Single)(object)values[3 + index];
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ fixed (Double* basePtr = &this.register.double_0)
+ {
+ *(basePtr + 0) = (Double)(object)values[0 + index];
+ *(basePtr + 1) = (Double)(object)values[1 + index];
+ }
+ }
+ }
+ }
+
+#pragma warning disable 3001 // void* is not a CLS-Compliant argument type
+ internal unsafe Vector(void* dataPointer) : this(dataPointer, 0) { }
+#pragma warning restore 3001 // void* is not a CLS-Compliant argument type
+
+#pragma warning disable 3001 // void* is not a CLS-Compliant argument type
+ // Implemented with offset if this API ever becomes public; an offset of 0 is used internally.
+ internal unsafe Vector(void* dataPointer, int offset)
+ : this()
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* castedPtr = (Byte*)dataPointer;
+ castedPtr += offset;
+ fixed (Byte* registerBase = &this.register.byte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* castedPtr = (SByte*)dataPointer;
+ castedPtr += offset;
+ fixed (SByte* registerBase = &this.register.sbyte_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* castedPtr = (UInt16*)dataPointer;
+ castedPtr += offset;
+ fixed (UInt16* registerBase = &this.register.uint16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* castedPtr = (Int16*)dataPointer;
+ castedPtr += offset;
+ fixed (Int16* registerBase = &this.register.int16_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* castedPtr = (UInt32*)dataPointer;
+ castedPtr += offset;
+ fixed (UInt32* registerBase = &this.register.uint32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* castedPtr = (Int32*)dataPointer;
+ castedPtr += offset;
+ fixed (Int32* registerBase = &this.register.int32_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* castedPtr = (UInt64*)dataPointer;
+ castedPtr += offset;
+ fixed (UInt64* registerBase = &this.register.uint64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* castedPtr = (Int64*)dataPointer;
+ castedPtr += offset;
+ fixed (Int64* registerBase = &this.register.int64_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* castedPtr = (Single*)dataPointer;
+ castedPtr += offset;
+ fixed (Single* registerBase = &this.register.single_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* castedPtr = (Double*)dataPointer;
+ castedPtr += offset;
+ fixed (Double* registerBase = &this.register.double_0)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+#pragma warning restore 3001 // void* is not a CLS-Compliant argument type
+
+ private Vector(ref Register existingRegister)
+ {
+ this.register = existingRegister;
+ }
+ #endregion Constructors
+
+ #region Public Instance Methods
+ /// <summary>
+ /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
+ /// </summary>
+ /// <param name="destination">The destination array which the values are copied into</param>
+ /// <exception cref="ArgumentNullException">If the destination array is null</exception>
+ /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination array</exception>
+ [Intrinsic]
+ public unsafe void CopyTo(T[] destination)
+ {
+ CopyTo(destination, 0);
+ }
+
+ /// <summary>
+ /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
+ /// </summary>
+ /// <param name="destination">The destination array which the values are copied into</param>
+ /// <param name="startIndex">The index to start copying to</param>
+ /// <exception cref="ArgumentNullException">If the destination array is null</exception>
+ /// <exception cref="ArgumentOutOfRangeException">If index is greater than end of the array or index is less than zero</exception>
+ /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination array</exception>
+ [Intrinsic]
+ public unsafe void CopyTo(T[] destination, int startIndex)
+ {
+ if (destination == null)
+ {
+ // Match the JIT's exception type here. For perf, a NullReference is thrown instead of an ArgumentNull.
+ throw new NullReferenceException(SR.Arg_NullArgumentNullRef);
+ }
+ if (startIndex < 0 || startIndex >= destination.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Format(SR.Arg_ArgumentOutOfRangeException, startIndex));
+ }
+ if ((destination.Length - startIndex) < Count)
+ {
+ throw new ArgumentException(SR.Format(SR.Arg_ElementsInSourceIsGreaterThanDestination, startIndex));
+ }
+
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte[] byteArray = (Byte[])(object)destination;
+ fixed (Byte* destinationBase = byteArray)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Byte)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte[] sbyteArray = (SByte[])(object)destination;
+ fixed (SByte* destinationBase = sbyteArray)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (SByte)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16[] uint16Array = (UInt16[])(object)destination;
+ fixed (UInt16* destinationBase = uint16Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (UInt16)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16[] int16Array = (Int16[])(object)destination;
+ fixed (Int16* destinationBase = int16Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Int16)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32[] uint32Array = (UInt32[])(object)destination;
+ fixed (UInt32* destinationBase = uint32Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (UInt32)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32[] int32Array = (Int32[])(object)destination;
+ fixed (Int32* destinationBase = int32Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Int32)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64[] uint64Array = (UInt64[])(object)destination;
+ fixed (UInt64* destinationBase = uint64Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (UInt64)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64[] int64Array = (Int64[])(object)destination;
+ fixed (Int64* destinationBase = int64Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Int64)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single[] singleArray = (Single[])(object)destination;
+ fixed (Single* destinationBase = singleArray)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Single)(object)this[g];
+ }
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double[] doubleArray = (Double[])(object)destination;
+ fixed (Double* destinationBase = doubleArray)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (Double)(object)this[g];
+ }
+ }
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte[] byteArray = (Byte[])(object)destination;
+ fixed (Byte* destinationBase = byteArray)
+ {
+ destinationBase[startIndex + 0] = this.register.byte_0;
+ destinationBase[startIndex + 1] = this.register.byte_1;
+ destinationBase[startIndex + 2] = this.register.byte_2;
+ destinationBase[startIndex + 3] = this.register.byte_3;
+ destinationBase[startIndex + 4] = this.register.byte_4;
+ destinationBase[startIndex + 5] = this.register.byte_5;
+ destinationBase[startIndex + 6] = this.register.byte_6;
+ destinationBase[startIndex + 7] = this.register.byte_7;
+ destinationBase[startIndex + 8] = this.register.byte_8;
+ destinationBase[startIndex + 9] = this.register.byte_9;
+ destinationBase[startIndex + 10] = this.register.byte_10;
+ destinationBase[startIndex + 11] = this.register.byte_11;
+ destinationBase[startIndex + 12] = this.register.byte_12;
+ destinationBase[startIndex + 13] = this.register.byte_13;
+ destinationBase[startIndex + 14] = this.register.byte_14;
+ destinationBase[startIndex + 15] = this.register.byte_15;
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte[] sbyteArray = (SByte[])(object)destination;
+ fixed (SByte* destinationBase = sbyteArray)
+ {
+ destinationBase[startIndex + 0] = this.register.sbyte_0;
+ destinationBase[startIndex + 1] = this.register.sbyte_1;
+ destinationBase[startIndex + 2] = this.register.sbyte_2;
+ destinationBase[startIndex + 3] = this.register.sbyte_3;
+ destinationBase[startIndex + 4] = this.register.sbyte_4;
+ destinationBase[startIndex + 5] = this.register.sbyte_5;
+ destinationBase[startIndex + 6] = this.register.sbyte_6;
+ destinationBase[startIndex + 7] = this.register.sbyte_7;
+ destinationBase[startIndex + 8] = this.register.sbyte_8;
+ destinationBase[startIndex + 9] = this.register.sbyte_9;
+ destinationBase[startIndex + 10] = this.register.sbyte_10;
+ destinationBase[startIndex + 11] = this.register.sbyte_11;
+ destinationBase[startIndex + 12] = this.register.sbyte_12;
+ destinationBase[startIndex + 13] = this.register.sbyte_13;
+ destinationBase[startIndex + 14] = this.register.sbyte_14;
+ destinationBase[startIndex + 15] = this.register.sbyte_15;
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16[] uint16Array = (UInt16[])(object)destination;
+ fixed (UInt16* destinationBase = uint16Array)
+ {
+ destinationBase[startIndex + 0] = this.register.uint16_0;
+ destinationBase[startIndex + 1] = this.register.uint16_1;
+ destinationBase[startIndex + 2] = this.register.uint16_2;
+ destinationBase[startIndex + 3] = this.register.uint16_3;
+ destinationBase[startIndex + 4] = this.register.uint16_4;
+ destinationBase[startIndex + 5] = this.register.uint16_5;
+ destinationBase[startIndex + 6] = this.register.uint16_6;
+ destinationBase[startIndex + 7] = this.register.uint16_7;
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16[] int16Array = (Int16[])(object)destination;
+ fixed (Int16* destinationBase = int16Array)
+ {
+ destinationBase[startIndex + 0] = this.register.int16_0;
+ destinationBase[startIndex + 1] = this.register.int16_1;
+ destinationBase[startIndex + 2] = this.register.int16_2;
+ destinationBase[startIndex + 3] = this.register.int16_3;
+ destinationBase[startIndex + 4] = this.register.int16_4;
+ destinationBase[startIndex + 5] = this.register.int16_5;
+ destinationBase[startIndex + 6] = this.register.int16_6;
+ destinationBase[startIndex + 7] = this.register.int16_7;
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32[] uint32Array = (UInt32[])(object)destination;
+ fixed (UInt32* destinationBase = uint32Array)
+ {
+ destinationBase[startIndex + 0] = this.register.uint32_0;
+ destinationBase[startIndex + 1] = this.register.uint32_1;
+ destinationBase[startIndex + 2] = this.register.uint32_2;
+ destinationBase[startIndex + 3] = this.register.uint32_3;
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32[] int32Array = (Int32[])(object)destination;
+ fixed (Int32* destinationBase = int32Array)
+ {
+ destinationBase[startIndex + 0] = this.register.int32_0;
+ destinationBase[startIndex + 1] = this.register.int32_1;
+ destinationBase[startIndex + 2] = this.register.int32_2;
+ destinationBase[startIndex + 3] = this.register.int32_3;
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64[] uint64Array = (UInt64[])(object)destination;
+ fixed (UInt64* destinationBase = uint64Array)
+ {
+ destinationBase[startIndex + 0] = this.register.uint64_0;
+ destinationBase[startIndex + 1] = this.register.uint64_1;
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64[] int64Array = (Int64[])(object)destination;
+ fixed (Int64* destinationBase = int64Array)
+ {
+ destinationBase[startIndex + 0] = this.register.int64_0;
+ destinationBase[startIndex + 1] = this.register.int64_1;
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single[] singleArray = (Single[])(object)destination;
+ fixed (Single* destinationBase = singleArray)
+ {
+ destinationBase[startIndex + 0] = this.register.single_0;
+ destinationBase[startIndex + 1] = this.register.single_1;
+ destinationBase[startIndex + 2] = this.register.single_2;
+ destinationBase[startIndex + 3] = this.register.single_3;
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double[] doubleArray = (Double[])(object)destination;
+ fixed (Double* destinationBase = doubleArray)
+ {
+ destinationBase[startIndex + 0] = this.register.double_0;
+ destinationBase[startIndex + 1] = this.register.double_1;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the element at the given index.
+ /// </summary>
+ public unsafe T this[int index]
+ {
+ [Intrinsic]
+ get
+ {
+ if (index >= Count || index < 0)
+ {
+ throw new IndexOutOfRangeException(SR.Format(SR.Arg_ArgumentOutOfRangeException, index));
+ }
+ if (typeof(T) == typeof(Byte))
+ {
+ fixed (Byte* basePtr = &this.register.byte_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ fixed (SByte* basePtr = &this.register.sbyte_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ fixed (UInt16* basePtr = &this.register.uint16_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ fixed (Int16* basePtr = &this.register.int16_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ fixed (UInt32* basePtr = &this.register.uint32_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ fixed (Int32* basePtr = &this.register.int32_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ fixed (UInt64* basePtr = &this.register.uint64_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ fixed (Int64* basePtr = &this.register.int64_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ fixed (Single* basePtr = &this.register.single_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ fixed (Double* basePtr = &this.register.double_0)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether the given Object is equal to this vector instance.
+ /// </summary>
+ /// <param name="obj">The Object to compare against.</param>
+ /// <returns>True if the Object is equal to this vector; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object obj)
+ {
+ if (!(obj is Vector<T>))
+ {
+ return false;
+ }
+ return Equals((Vector<T>)obj);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether the given vector is equal to this vector instance.
+ /// </summary>
+ /// <param name="other">The vector to compare this instance to.</param>
+ /// <returns>True if the other vector is equal to this instance; False otherwise.</returns>
+ [Intrinsic]
+ public bool Equals(Vector<T> other)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ if (!ScalarEquals(this[g], other[g]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return
+ this.register.byte_0 == other.register.byte_0
+ && this.register.byte_1 == other.register.byte_1
+ && this.register.byte_2 == other.register.byte_2
+ && this.register.byte_3 == other.register.byte_3
+ && this.register.byte_4 == other.register.byte_4
+ && this.register.byte_5 == other.register.byte_5
+ && this.register.byte_6 == other.register.byte_6
+ && this.register.byte_7 == other.register.byte_7
+ && this.register.byte_8 == other.register.byte_8
+ && this.register.byte_9 == other.register.byte_9
+ && this.register.byte_10 == other.register.byte_10
+ && this.register.byte_11 == other.register.byte_11
+ && this.register.byte_12 == other.register.byte_12
+ && this.register.byte_13 == other.register.byte_13
+ && this.register.byte_14 == other.register.byte_14
+ && this.register.byte_15 == other.register.byte_15;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return
+ this.register.sbyte_0 == other.register.sbyte_0
+ && this.register.sbyte_1 == other.register.sbyte_1
+ && this.register.sbyte_2 == other.register.sbyte_2
+ && this.register.sbyte_3 == other.register.sbyte_3
+ && this.register.sbyte_4 == other.register.sbyte_4
+ && this.register.sbyte_5 == other.register.sbyte_5
+ && this.register.sbyte_6 == other.register.sbyte_6
+ && this.register.sbyte_7 == other.register.sbyte_7
+ && this.register.sbyte_8 == other.register.sbyte_8
+ && this.register.sbyte_9 == other.register.sbyte_9
+ && this.register.sbyte_10 == other.register.sbyte_10
+ && this.register.sbyte_11 == other.register.sbyte_11
+ && this.register.sbyte_12 == other.register.sbyte_12
+ && this.register.sbyte_13 == other.register.sbyte_13
+ && this.register.sbyte_14 == other.register.sbyte_14
+ && this.register.sbyte_15 == other.register.sbyte_15;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return
+ this.register.uint16_0 == other.register.uint16_0
+ && this.register.uint16_1 == other.register.uint16_1
+ && this.register.uint16_2 == other.register.uint16_2
+ && this.register.uint16_3 == other.register.uint16_3
+ && this.register.uint16_4 == other.register.uint16_4
+ && this.register.uint16_5 == other.register.uint16_5
+ && this.register.uint16_6 == other.register.uint16_6
+ && this.register.uint16_7 == other.register.uint16_7;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return
+ this.register.int16_0 == other.register.int16_0
+ && this.register.int16_1 == other.register.int16_1
+ && this.register.int16_2 == other.register.int16_2
+ && this.register.int16_3 == other.register.int16_3
+ && this.register.int16_4 == other.register.int16_4
+ && this.register.int16_5 == other.register.int16_5
+ && this.register.int16_6 == other.register.int16_6
+ && this.register.int16_7 == other.register.int16_7;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return
+ this.register.uint32_0 == other.register.uint32_0
+ && this.register.uint32_1 == other.register.uint32_1
+ && this.register.uint32_2 == other.register.uint32_2
+ && this.register.uint32_3 == other.register.uint32_3;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return
+ this.register.int32_0 == other.register.int32_0
+ && this.register.int32_1 == other.register.int32_1
+ && this.register.int32_2 == other.register.int32_2
+ && this.register.int32_3 == other.register.int32_3;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return
+ this.register.uint64_0 == other.register.uint64_0
+ && this.register.uint64_1 == other.register.uint64_1;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return
+ this.register.int64_0 == other.register.int64_0
+ && this.register.int64_1 == other.register.int64_1;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return
+ this.register.single_0 == other.register.single_0
+ && this.register.single_1 == other.register.single_1
+ && this.register.single_2 == other.register.single_2
+ && this.register.single_3 == other.register.single_3;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return
+ this.register.double_0 == other.register.double_0
+ && this.register.double_1 == other.register.double_1;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the hash code for this instance.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ public override int GetHashCode()
+ {
+ int hash = 0;
+
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Byte)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((SByte)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((UInt16)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Int16)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((UInt32)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Int32)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((UInt64)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Int64)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Single)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((Double)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ hash = HashHelpers.Combine(hash, this.register.byte_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_3.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_4.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_5.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_6.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_7.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_8.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_9.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_10.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_11.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_12.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_13.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_14.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.byte_15.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ hash = HashHelpers.Combine(hash, this.register.sbyte_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_3.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_4.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_5.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_6.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_7.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_8.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_9.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_10.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_11.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_12.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_13.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_14.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.sbyte_15.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ hash = HashHelpers.Combine(hash, this.register.uint16_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_3.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_4.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_5.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_6.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint16_7.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ hash = HashHelpers.Combine(hash, this.register.int16_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_3.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_4.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_5.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_6.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int16_7.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ hash = HashHelpers.Combine(hash, this.register.uint32_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint32_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint32_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint32_3.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ hash = HashHelpers.Combine(hash, this.register.int32_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int32_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int32_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int32_3.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ hash = HashHelpers.Combine(hash, this.register.uint64_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.uint64_1.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ hash = HashHelpers.Combine(hash, this.register.int64_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.int64_1.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ hash = HashHelpers.Combine(hash, this.register.single_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.single_1.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.single_2.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.single_3.GetHashCode());
+ return hash;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ hash = HashHelpers.Combine(hash, this.register.double_0.GetHashCode());
+ hash = HashHelpers.Combine(hash, this.register.double_1.GetHashCode());
+ return hash;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector.
+ /// </summary>
+ /// <returns>The string representation.</returns>
+ public override string ToString()
+ {
+ return ToString("G", CultureInfo.CurrentCulture);
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector, using the specified format string to format individual elements.
+ /// </summary>
+ /// <param name="format">The format of individual elements.</param>
+ /// <returns>The string representation.</returns>
+ public string ToString(string format)
+ {
+ return ToString(format, CultureInfo.CurrentCulture);
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector, using the specified format string to format individual elements
+ /// and the given IFormatProvider.
+ /// </summary>
+ /// <param name="format">The format of individual elements.</param>
+ /// <param name="formatProvider">The format provider to use when formatting elements.</param>
+ /// <returns>The string representation.</returns>
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ StringBuilder sb = new StringBuilder();
+ string separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator;
+ sb.Append('<');
+ for (int g = 0; g < Count - 1; g++)
+ {
+ sb.Append(((IFormattable)this[g]).ToString(format, formatProvider));
+ sb.Append(separator);
+ sb.Append(' ');
+ }
+ // Append last element w/out separator
+ sb.Append(((IFormattable)this[Count - 1]).ToString(format, formatProvider));
+ sb.Append('>');
+ return sb.ToString();
+ }
+ #endregion Public Instance Methods
+
+ #region Arithmetic Operators
+ /// <summary>
+ /// Adds two vectors together.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The summed vector.</returns>
+ public static unsafe Vector<T> operator +(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Byte)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (SByte)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt16)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int16)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt32)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int32)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt64)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int64)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Single)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Double)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> sum = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ sum.register.byte_0 = (Byte)(left.register.byte_0 + right.register.byte_0);
+ sum.register.byte_1 = (Byte)(left.register.byte_1 + right.register.byte_1);
+ sum.register.byte_2 = (Byte)(left.register.byte_2 + right.register.byte_2);
+ sum.register.byte_3 = (Byte)(left.register.byte_3 + right.register.byte_3);
+ sum.register.byte_4 = (Byte)(left.register.byte_4 + right.register.byte_4);
+ sum.register.byte_5 = (Byte)(left.register.byte_5 + right.register.byte_5);
+ sum.register.byte_6 = (Byte)(left.register.byte_6 + right.register.byte_6);
+ sum.register.byte_7 = (Byte)(left.register.byte_7 + right.register.byte_7);
+ sum.register.byte_8 = (Byte)(left.register.byte_8 + right.register.byte_8);
+ sum.register.byte_9 = (Byte)(left.register.byte_9 + right.register.byte_9);
+ sum.register.byte_10 = (Byte)(left.register.byte_10 + right.register.byte_10);
+ sum.register.byte_11 = (Byte)(left.register.byte_11 + right.register.byte_11);
+ sum.register.byte_12 = (Byte)(left.register.byte_12 + right.register.byte_12);
+ sum.register.byte_13 = (Byte)(left.register.byte_13 + right.register.byte_13);
+ sum.register.byte_14 = (Byte)(left.register.byte_14 + right.register.byte_14);
+ sum.register.byte_15 = (Byte)(left.register.byte_15 + right.register.byte_15);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ sum.register.sbyte_0 = (SByte)(left.register.sbyte_0 + right.register.sbyte_0);
+ sum.register.sbyte_1 = (SByte)(left.register.sbyte_1 + right.register.sbyte_1);
+ sum.register.sbyte_2 = (SByte)(left.register.sbyte_2 + right.register.sbyte_2);
+ sum.register.sbyte_3 = (SByte)(left.register.sbyte_3 + right.register.sbyte_3);
+ sum.register.sbyte_4 = (SByte)(left.register.sbyte_4 + right.register.sbyte_4);
+ sum.register.sbyte_5 = (SByte)(left.register.sbyte_5 + right.register.sbyte_5);
+ sum.register.sbyte_6 = (SByte)(left.register.sbyte_6 + right.register.sbyte_6);
+ sum.register.sbyte_7 = (SByte)(left.register.sbyte_7 + right.register.sbyte_7);
+ sum.register.sbyte_8 = (SByte)(left.register.sbyte_8 + right.register.sbyte_8);
+ sum.register.sbyte_9 = (SByte)(left.register.sbyte_9 + right.register.sbyte_9);
+ sum.register.sbyte_10 = (SByte)(left.register.sbyte_10 + right.register.sbyte_10);
+ sum.register.sbyte_11 = (SByte)(left.register.sbyte_11 + right.register.sbyte_11);
+ sum.register.sbyte_12 = (SByte)(left.register.sbyte_12 + right.register.sbyte_12);
+ sum.register.sbyte_13 = (SByte)(left.register.sbyte_13 + right.register.sbyte_13);
+ sum.register.sbyte_14 = (SByte)(left.register.sbyte_14 + right.register.sbyte_14);
+ sum.register.sbyte_15 = (SByte)(left.register.sbyte_15 + right.register.sbyte_15);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ sum.register.uint16_0 = (UInt16)(left.register.uint16_0 + right.register.uint16_0);
+ sum.register.uint16_1 = (UInt16)(left.register.uint16_1 + right.register.uint16_1);
+ sum.register.uint16_2 = (UInt16)(left.register.uint16_2 + right.register.uint16_2);
+ sum.register.uint16_3 = (UInt16)(left.register.uint16_3 + right.register.uint16_3);
+ sum.register.uint16_4 = (UInt16)(left.register.uint16_4 + right.register.uint16_4);
+ sum.register.uint16_5 = (UInt16)(left.register.uint16_5 + right.register.uint16_5);
+ sum.register.uint16_6 = (UInt16)(left.register.uint16_6 + right.register.uint16_6);
+ sum.register.uint16_7 = (UInt16)(left.register.uint16_7 + right.register.uint16_7);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ sum.register.int16_0 = (Int16)(left.register.int16_0 + right.register.int16_0);
+ sum.register.int16_1 = (Int16)(left.register.int16_1 + right.register.int16_1);
+ sum.register.int16_2 = (Int16)(left.register.int16_2 + right.register.int16_2);
+ sum.register.int16_3 = (Int16)(left.register.int16_3 + right.register.int16_3);
+ sum.register.int16_4 = (Int16)(left.register.int16_4 + right.register.int16_4);
+ sum.register.int16_5 = (Int16)(left.register.int16_5 + right.register.int16_5);
+ sum.register.int16_6 = (Int16)(left.register.int16_6 + right.register.int16_6);
+ sum.register.int16_7 = (Int16)(left.register.int16_7 + right.register.int16_7);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ sum.register.uint32_0 = (UInt32)(left.register.uint32_0 + right.register.uint32_0);
+ sum.register.uint32_1 = (UInt32)(left.register.uint32_1 + right.register.uint32_1);
+ sum.register.uint32_2 = (UInt32)(left.register.uint32_2 + right.register.uint32_2);
+ sum.register.uint32_3 = (UInt32)(left.register.uint32_3 + right.register.uint32_3);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ sum.register.int32_0 = (Int32)(left.register.int32_0 + right.register.int32_0);
+ sum.register.int32_1 = (Int32)(left.register.int32_1 + right.register.int32_1);
+ sum.register.int32_2 = (Int32)(left.register.int32_2 + right.register.int32_2);
+ sum.register.int32_3 = (Int32)(left.register.int32_3 + right.register.int32_3);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ sum.register.uint64_0 = (UInt64)(left.register.uint64_0 + right.register.uint64_0);
+ sum.register.uint64_1 = (UInt64)(left.register.uint64_1 + right.register.uint64_1);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ sum.register.int64_0 = (Int64)(left.register.int64_0 + right.register.int64_0);
+ sum.register.int64_1 = (Int64)(left.register.int64_1 + right.register.int64_1);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ sum.register.single_0 = (Single)(left.register.single_0 + right.register.single_0);
+ sum.register.single_1 = (Single)(left.register.single_1 + right.register.single_1);
+ sum.register.single_2 = (Single)(left.register.single_2 + right.register.single_2);
+ sum.register.single_3 = (Single)(left.register.single_3 + right.register.single_3);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ sum.register.double_0 = (Double)(left.register.double_0 + right.register.double_0);
+ sum.register.double_1 = (Double)(left.register.double_1 + right.register.double_1);
+ }
+ return sum;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Subtracts the second vector from the first.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The difference vector.</returns>
+ public static unsafe Vector<T> operator -(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Byte)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (SByte)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt16)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int16)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt32)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int32)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt64)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int64)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Single)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Double)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> difference = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ difference.register.byte_0 = (Byte)(left.register.byte_0 - right.register.byte_0);
+ difference.register.byte_1 = (Byte)(left.register.byte_1 - right.register.byte_1);
+ difference.register.byte_2 = (Byte)(left.register.byte_2 - right.register.byte_2);
+ difference.register.byte_3 = (Byte)(left.register.byte_3 - right.register.byte_3);
+ difference.register.byte_4 = (Byte)(left.register.byte_4 - right.register.byte_4);
+ difference.register.byte_5 = (Byte)(left.register.byte_5 - right.register.byte_5);
+ difference.register.byte_6 = (Byte)(left.register.byte_6 - right.register.byte_6);
+ difference.register.byte_7 = (Byte)(left.register.byte_7 - right.register.byte_7);
+ difference.register.byte_8 = (Byte)(left.register.byte_8 - right.register.byte_8);
+ difference.register.byte_9 = (Byte)(left.register.byte_9 - right.register.byte_9);
+ difference.register.byte_10 = (Byte)(left.register.byte_10 - right.register.byte_10);
+ difference.register.byte_11 = (Byte)(left.register.byte_11 - right.register.byte_11);
+ difference.register.byte_12 = (Byte)(left.register.byte_12 - right.register.byte_12);
+ difference.register.byte_13 = (Byte)(left.register.byte_13 - right.register.byte_13);
+ difference.register.byte_14 = (Byte)(left.register.byte_14 - right.register.byte_14);
+ difference.register.byte_15 = (Byte)(left.register.byte_15 - right.register.byte_15);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ difference.register.sbyte_0 = (SByte)(left.register.sbyte_0 - right.register.sbyte_0);
+ difference.register.sbyte_1 = (SByte)(left.register.sbyte_1 - right.register.sbyte_1);
+ difference.register.sbyte_2 = (SByte)(left.register.sbyte_2 - right.register.sbyte_2);
+ difference.register.sbyte_3 = (SByte)(left.register.sbyte_3 - right.register.sbyte_3);
+ difference.register.sbyte_4 = (SByte)(left.register.sbyte_4 - right.register.sbyte_4);
+ difference.register.sbyte_5 = (SByte)(left.register.sbyte_5 - right.register.sbyte_5);
+ difference.register.sbyte_6 = (SByte)(left.register.sbyte_6 - right.register.sbyte_6);
+ difference.register.sbyte_7 = (SByte)(left.register.sbyte_7 - right.register.sbyte_7);
+ difference.register.sbyte_8 = (SByte)(left.register.sbyte_8 - right.register.sbyte_8);
+ difference.register.sbyte_9 = (SByte)(left.register.sbyte_9 - right.register.sbyte_9);
+ difference.register.sbyte_10 = (SByte)(left.register.sbyte_10 - right.register.sbyte_10);
+ difference.register.sbyte_11 = (SByte)(left.register.sbyte_11 - right.register.sbyte_11);
+ difference.register.sbyte_12 = (SByte)(left.register.sbyte_12 - right.register.sbyte_12);
+ difference.register.sbyte_13 = (SByte)(left.register.sbyte_13 - right.register.sbyte_13);
+ difference.register.sbyte_14 = (SByte)(left.register.sbyte_14 - right.register.sbyte_14);
+ difference.register.sbyte_15 = (SByte)(left.register.sbyte_15 - right.register.sbyte_15);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ difference.register.uint16_0 = (UInt16)(left.register.uint16_0 - right.register.uint16_0);
+ difference.register.uint16_1 = (UInt16)(left.register.uint16_1 - right.register.uint16_1);
+ difference.register.uint16_2 = (UInt16)(left.register.uint16_2 - right.register.uint16_2);
+ difference.register.uint16_3 = (UInt16)(left.register.uint16_3 - right.register.uint16_3);
+ difference.register.uint16_4 = (UInt16)(left.register.uint16_4 - right.register.uint16_4);
+ difference.register.uint16_5 = (UInt16)(left.register.uint16_5 - right.register.uint16_5);
+ difference.register.uint16_6 = (UInt16)(left.register.uint16_6 - right.register.uint16_6);
+ difference.register.uint16_7 = (UInt16)(left.register.uint16_7 - right.register.uint16_7);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ difference.register.int16_0 = (Int16)(left.register.int16_0 - right.register.int16_0);
+ difference.register.int16_1 = (Int16)(left.register.int16_1 - right.register.int16_1);
+ difference.register.int16_2 = (Int16)(left.register.int16_2 - right.register.int16_2);
+ difference.register.int16_3 = (Int16)(left.register.int16_3 - right.register.int16_3);
+ difference.register.int16_4 = (Int16)(left.register.int16_4 - right.register.int16_4);
+ difference.register.int16_5 = (Int16)(left.register.int16_5 - right.register.int16_5);
+ difference.register.int16_6 = (Int16)(left.register.int16_6 - right.register.int16_6);
+ difference.register.int16_7 = (Int16)(left.register.int16_7 - right.register.int16_7);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ difference.register.uint32_0 = (UInt32)(left.register.uint32_0 - right.register.uint32_0);
+ difference.register.uint32_1 = (UInt32)(left.register.uint32_1 - right.register.uint32_1);
+ difference.register.uint32_2 = (UInt32)(left.register.uint32_2 - right.register.uint32_2);
+ difference.register.uint32_3 = (UInt32)(left.register.uint32_3 - right.register.uint32_3);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ difference.register.int32_0 = (Int32)(left.register.int32_0 - right.register.int32_0);
+ difference.register.int32_1 = (Int32)(left.register.int32_1 - right.register.int32_1);
+ difference.register.int32_2 = (Int32)(left.register.int32_2 - right.register.int32_2);
+ difference.register.int32_3 = (Int32)(left.register.int32_3 - right.register.int32_3);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ difference.register.uint64_0 = (UInt64)(left.register.uint64_0 - right.register.uint64_0);
+ difference.register.uint64_1 = (UInt64)(left.register.uint64_1 - right.register.uint64_1);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ difference.register.int64_0 = (Int64)(left.register.int64_0 - right.register.int64_0);
+ difference.register.int64_1 = (Int64)(left.register.int64_1 - right.register.int64_1);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ difference.register.single_0 = (Single)(left.register.single_0 - right.register.single_0);
+ difference.register.single_1 = (Single)(left.register.single_1 - right.register.single_1);
+ difference.register.single_2 = (Single)(left.register.single_2 - right.register.single_2);
+ difference.register.single_3 = (Single)(left.register.single_3 - right.register.single_3);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ difference.register.double_0 = (Double)(left.register.double_0 - right.register.double_0);
+ difference.register.double_1 = (Double)(left.register.double_1 - right.register.double_1);
+ }
+ return difference;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies two vectors together.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The product vector.</returns>
+ public static unsafe Vector<T> operator *(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Byte)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (SByte)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt16)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int16)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt32)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int32)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt64)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int64)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Single)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Double)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ product.register.byte_0 = (Byte)(left.register.byte_0 * right.register.byte_0);
+ product.register.byte_1 = (Byte)(left.register.byte_1 * right.register.byte_1);
+ product.register.byte_2 = (Byte)(left.register.byte_2 * right.register.byte_2);
+ product.register.byte_3 = (Byte)(left.register.byte_3 * right.register.byte_3);
+ product.register.byte_4 = (Byte)(left.register.byte_4 * right.register.byte_4);
+ product.register.byte_5 = (Byte)(left.register.byte_5 * right.register.byte_5);
+ product.register.byte_6 = (Byte)(left.register.byte_6 * right.register.byte_6);
+ product.register.byte_7 = (Byte)(left.register.byte_7 * right.register.byte_7);
+ product.register.byte_8 = (Byte)(left.register.byte_8 * right.register.byte_8);
+ product.register.byte_9 = (Byte)(left.register.byte_9 * right.register.byte_9);
+ product.register.byte_10 = (Byte)(left.register.byte_10 * right.register.byte_10);
+ product.register.byte_11 = (Byte)(left.register.byte_11 * right.register.byte_11);
+ product.register.byte_12 = (Byte)(left.register.byte_12 * right.register.byte_12);
+ product.register.byte_13 = (Byte)(left.register.byte_13 * right.register.byte_13);
+ product.register.byte_14 = (Byte)(left.register.byte_14 * right.register.byte_14);
+ product.register.byte_15 = (Byte)(left.register.byte_15 * right.register.byte_15);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ product.register.sbyte_0 = (SByte)(left.register.sbyte_0 * right.register.sbyte_0);
+ product.register.sbyte_1 = (SByte)(left.register.sbyte_1 * right.register.sbyte_1);
+ product.register.sbyte_2 = (SByte)(left.register.sbyte_2 * right.register.sbyte_2);
+ product.register.sbyte_3 = (SByte)(left.register.sbyte_3 * right.register.sbyte_3);
+ product.register.sbyte_4 = (SByte)(left.register.sbyte_4 * right.register.sbyte_4);
+ product.register.sbyte_5 = (SByte)(left.register.sbyte_5 * right.register.sbyte_5);
+ product.register.sbyte_6 = (SByte)(left.register.sbyte_6 * right.register.sbyte_6);
+ product.register.sbyte_7 = (SByte)(left.register.sbyte_7 * right.register.sbyte_7);
+ product.register.sbyte_8 = (SByte)(left.register.sbyte_8 * right.register.sbyte_8);
+ product.register.sbyte_9 = (SByte)(left.register.sbyte_9 * right.register.sbyte_9);
+ product.register.sbyte_10 = (SByte)(left.register.sbyte_10 * right.register.sbyte_10);
+ product.register.sbyte_11 = (SByte)(left.register.sbyte_11 * right.register.sbyte_11);
+ product.register.sbyte_12 = (SByte)(left.register.sbyte_12 * right.register.sbyte_12);
+ product.register.sbyte_13 = (SByte)(left.register.sbyte_13 * right.register.sbyte_13);
+ product.register.sbyte_14 = (SByte)(left.register.sbyte_14 * right.register.sbyte_14);
+ product.register.sbyte_15 = (SByte)(left.register.sbyte_15 * right.register.sbyte_15);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ product.register.uint16_0 = (UInt16)(left.register.uint16_0 * right.register.uint16_0);
+ product.register.uint16_1 = (UInt16)(left.register.uint16_1 * right.register.uint16_1);
+ product.register.uint16_2 = (UInt16)(left.register.uint16_2 * right.register.uint16_2);
+ product.register.uint16_3 = (UInt16)(left.register.uint16_3 * right.register.uint16_3);
+ product.register.uint16_4 = (UInt16)(left.register.uint16_4 * right.register.uint16_4);
+ product.register.uint16_5 = (UInt16)(left.register.uint16_5 * right.register.uint16_5);
+ product.register.uint16_6 = (UInt16)(left.register.uint16_6 * right.register.uint16_6);
+ product.register.uint16_7 = (UInt16)(left.register.uint16_7 * right.register.uint16_7);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ product.register.int16_0 = (Int16)(left.register.int16_0 * right.register.int16_0);
+ product.register.int16_1 = (Int16)(left.register.int16_1 * right.register.int16_1);
+ product.register.int16_2 = (Int16)(left.register.int16_2 * right.register.int16_2);
+ product.register.int16_3 = (Int16)(left.register.int16_3 * right.register.int16_3);
+ product.register.int16_4 = (Int16)(left.register.int16_4 * right.register.int16_4);
+ product.register.int16_5 = (Int16)(left.register.int16_5 * right.register.int16_5);
+ product.register.int16_6 = (Int16)(left.register.int16_6 * right.register.int16_6);
+ product.register.int16_7 = (Int16)(left.register.int16_7 * right.register.int16_7);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ product.register.uint32_0 = (UInt32)(left.register.uint32_0 * right.register.uint32_0);
+ product.register.uint32_1 = (UInt32)(left.register.uint32_1 * right.register.uint32_1);
+ product.register.uint32_2 = (UInt32)(left.register.uint32_2 * right.register.uint32_2);
+ product.register.uint32_3 = (UInt32)(left.register.uint32_3 * right.register.uint32_3);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ product.register.int32_0 = (Int32)(left.register.int32_0 * right.register.int32_0);
+ product.register.int32_1 = (Int32)(left.register.int32_1 * right.register.int32_1);
+ product.register.int32_2 = (Int32)(left.register.int32_2 * right.register.int32_2);
+ product.register.int32_3 = (Int32)(left.register.int32_3 * right.register.int32_3);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ product.register.uint64_0 = (UInt64)(left.register.uint64_0 * right.register.uint64_0);
+ product.register.uint64_1 = (UInt64)(left.register.uint64_1 * right.register.uint64_1);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ product.register.int64_0 = (Int64)(left.register.int64_0 * right.register.int64_0);
+ product.register.int64_1 = (Int64)(left.register.int64_1 * right.register.int64_1);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ product.register.single_0 = (Single)(left.register.single_0 * right.register.single_0);
+ product.register.single_1 = (Single)(left.register.single_1 * right.register.single_1);
+ product.register.single_2 = (Single)(left.register.single_2 * right.register.single_2);
+ product.register.single_3 = (Single)(left.register.single_3 * right.register.single_3);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ product.register.double_0 = (Double)(left.register.double_0 * right.register.double_0);
+ product.register.double_1 = (Double)(left.register.double_1 * right.register.double_1);
+ }
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies a vector by the given scalar.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <param name="factor">The scalar value.</param>
+ /// <returns>The scaled vector.</returns>
+ public static Vector<T> operator *(Vector<T> value, T factor)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ return new Vector<T>(factor) * value;
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ product.register.byte_0 = (Byte)(value.register.byte_0 * (Byte)(object)factor);
+ product.register.byte_1 = (Byte)(value.register.byte_1 * (Byte)(object)factor);
+ product.register.byte_2 = (Byte)(value.register.byte_2 * (Byte)(object)factor);
+ product.register.byte_3 = (Byte)(value.register.byte_3 * (Byte)(object)factor);
+ product.register.byte_4 = (Byte)(value.register.byte_4 * (Byte)(object)factor);
+ product.register.byte_5 = (Byte)(value.register.byte_5 * (Byte)(object)factor);
+ product.register.byte_6 = (Byte)(value.register.byte_6 * (Byte)(object)factor);
+ product.register.byte_7 = (Byte)(value.register.byte_7 * (Byte)(object)factor);
+ product.register.byte_8 = (Byte)(value.register.byte_8 * (Byte)(object)factor);
+ product.register.byte_9 = (Byte)(value.register.byte_9 * (Byte)(object)factor);
+ product.register.byte_10 = (Byte)(value.register.byte_10 * (Byte)(object)factor);
+ product.register.byte_11 = (Byte)(value.register.byte_11 * (Byte)(object)factor);
+ product.register.byte_12 = (Byte)(value.register.byte_12 * (Byte)(object)factor);
+ product.register.byte_13 = (Byte)(value.register.byte_13 * (Byte)(object)factor);
+ product.register.byte_14 = (Byte)(value.register.byte_14 * (Byte)(object)factor);
+ product.register.byte_15 = (Byte)(value.register.byte_15 * (Byte)(object)factor);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ product.register.sbyte_0 = (SByte)(value.register.sbyte_0 * (SByte)(object)factor);
+ product.register.sbyte_1 = (SByte)(value.register.sbyte_1 * (SByte)(object)factor);
+ product.register.sbyte_2 = (SByte)(value.register.sbyte_2 * (SByte)(object)factor);
+ product.register.sbyte_3 = (SByte)(value.register.sbyte_3 * (SByte)(object)factor);
+ product.register.sbyte_4 = (SByte)(value.register.sbyte_4 * (SByte)(object)factor);
+ product.register.sbyte_5 = (SByte)(value.register.sbyte_5 * (SByte)(object)factor);
+ product.register.sbyte_6 = (SByte)(value.register.sbyte_6 * (SByte)(object)factor);
+ product.register.sbyte_7 = (SByte)(value.register.sbyte_7 * (SByte)(object)factor);
+ product.register.sbyte_8 = (SByte)(value.register.sbyte_8 * (SByte)(object)factor);
+ product.register.sbyte_9 = (SByte)(value.register.sbyte_9 * (SByte)(object)factor);
+ product.register.sbyte_10 = (SByte)(value.register.sbyte_10 * (SByte)(object)factor);
+ product.register.sbyte_11 = (SByte)(value.register.sbyte_11 * (SByte)(object)factor);
+ product.register.sbyte_12 = (SByte)(value.register.sbyte_12 * (SByte)(object)factor);
+ product.register.sbyte_13 = (SByte)(value.register.sbyte_13 * (SByte)(object)factor);
+ product.register.sbyte_14 = (SByte)(value.register.sbyte_14 * (SByte)(object)factor);
+ product.register.sbyte_15 = (SByte)(value.register.sbyte_15 * (SByte)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ product.register.uint16_0 = (UInt16)(value.register.uint16_0 * (UInt16)(object)factor);
+ product.register.uint16_1 = (UInt16)(value.register.uint16_1 * (UInt16)(object)factor);
+ product.register.uint16_2 = (UInt16)(value.register.uint16_2 * (UInt16)(object)factor);
+ product.register.uint16_3 = (UInt16)(value.register.uint16_3 * (UInt16)(object)factor);
+ product.register.uint16_4 = (UInt16)(value.register.uint16_4 * (UInt16)(object)factor);
+ product.register.uint16_5 = (UInt16)(value.register.uint16_5 * (UInt16)(object)factor);
+ product.register.uint16_6 = (UInt16)(value.register.uint16_6 * (UInt16)(object)factor);
+ product.register.uint16_7 = (UInt16)(value.register.uint16_7 * (UInt16)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ product.register.int16_0 = (Int16)(value.register.int16_0 * (Int16)(object)factor);
+ product.register.int16_1 = (Int16)(value.register.int16_1 * (Int16)(object)factor);
+ product.register.int16_2 = (Int16)(value.register.int16_2 * (Int16)(object)factor);
+ product.register.int16_3 = (Int16)(value.register.int16_3 * (Int16)(object)factor);
+ product.register.int16_4 = (Int16)(value.register.int16_4 * (Int16)(object)factor);
+ product.register.int16_5 = (Int16)(value.register.int16_5 * (Int16)(object)factor);
+ product.register.int16_6 = (Int16)(value.register.int16_6 * (Int16)(object)factor);
+ product.register.int16_7 = (Int16)(value.register.int16_7 * (Int16)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ product.register.uint32_0 = (UInt32)(value.register.uint32_0 * (UInt32)(object)factor);
+ product.register.uint32_1 = (UInt32)(value.register.uint32_1 * (UInt32)(object)factor);
+ product.register.uint32_2 = (UInt32)(value.register.uint32_2 * (UInt32)(object)factor);
+ product.register.uint32_3 = (UInt32)(value.register.uint32_3 * (UInt32)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ product.register.int32_0 = (Int32)(value.register.int32_0 * (Int32)(object)factor);
+ product.register.int32_1 = (Int32)(value.register.int32_1 * (Int32)(object)factor);
+ product.register.int32_2 = (Int32)(value.register.int32_2 * (Int32)(object)factor);
+ product.register.int32_3 = (Int32)(value.register.int32_3 * (Int32)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ product.register.uint64_0 = (UInt64)(value.register.uint64_0 * (UInt64)(object)factor);
+ product.register.uint64_1 = (UInt64)(value.register.uint64_1 * (UInt64)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ product.register.int64_0 = (Int64)(value.register.int64_0 * (Int64)(object)factor);
+ product.register.int64_1 = (Int64)(value.register.int64_1 * (Int64)(object)factor);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ product.register.single_0 = (Single)(value.register.single_0 * (Single)(object)factor);
+ product.register.single_1 = (Single)(value.register.single_1 * (Single)(object)factor);
+ product.register.single_2 = (Single)(value.register.single_2 * (Single)(object)factor);
+ product.register.single_3 = (Single)(value.register.single_3 * (Single)(object)factor);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ product.register.double_0 = (Double)(value.register.double_0 * (Double)(object)factor);
+ product.register.double_1 = (Double)(value.register.double_1 * (Double)(object)factor);
+ }
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies a vector by the given scalar.
+ /// </summary>
+ /// <param name="factor">The scalar value.</param>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The scaled vector.</returns>
+ public static Vector<T> operator *(T factor, Vector<T> value)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ return new Vector<T>(factor) * value;
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ product.register.byte_0 = (Byte)(value.register.byte_0 * (Byte)(object)factor);
+ product.register.byte_1 = (Byte)(value.register.byte_1 * (Byte)(object)factor);
+ product.register.byte_2 = (Byte)(value.register.byte_2 * (Byte)(object)factor);
+ product.register.byte_3 = (Byte)(value.register.byte_3 * (Byte)(object)factor);
+ product.register.byte_4 = (Byte)(value.register.byte_4 * (Byte)(object)factor);
+ product.register.byte_5 = (Byte)(value.register.byte_5 * (Byte)(object)factor);
+ product.register.byte_6 = (Byte)(value.register.byte_6 * (Byte)(object)factor);
+ product.register.byte_7 = (Byte)(value.register.byte_7 * (Byte)(object)factor);
+ product.register.byte_8 = (Byte)(value.register.byte_8 * (Byte)(object)factor);
+ product.register.byte_9 = (Byte)(value.register.byte_9 * (Byte)(object)factor);
+ product.register.byte_10 = (Byte)(value.register.byte_10 * (Byte)(object)factor);
+ product.register.byte_11 = (Byte)(value.register.byte_11 * (Byte)(object)factor);
+ product.register.byte_12 = (Byte)(value.register.byte_12 * (Byte)(object)factor);
+ product.register.byte_13 = (Byte)(value.register.byte_13 * (Byte)(object)factor);
+ product.register.byte_14 = (Byte)(value.register.byte_14 * (Byte)(object)factor);
+ product.register.byte_15 = (Byte)(value.register.byte_15 * (Byte)(object)factor);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ product.register.sbyte_0 = (SByte)(value.register.sbyte_0 * (SByte)(object)factor);
+ product.register.sbyte_1 = (SByte)(value.register.sbyte_1 * (SByte)(object)factor);
+ product.register.sbyte_2 = (SByte)(value.register.sbyte_2 * (SByte)(object)factor);
+ product.register.sbyte_3 = (SByte)(value.register.sbyte_3 * (SByte)(object)factor);
+ product.register.sbyte_4 = (SByte)(value.register.sbyte_4 * (SByte)(object)factor);
+ product.register.sbyte_5 = (SByte)(value.register.sbyte_5 * (SByte)(object)factor);
+ product.register.sbyte_6 = (SByte)(value.register.sbyte_6 * (SByte)(object)factor);
+ product.register.sbyte_7 = (SByte)(value.register.sbyte_7 * (SByte)(object)factor);
+ product.register.sbyte_8 = (SByte)(value.register.sbyte_8 * (SByte)(object)factor);
+ product.register.sbyte_9 = (SByte)(value.register.sbyte_9 * (SByte)(object)factor);
+ product.register.sbyte_10 = (SByte)(value.register.sbyte_10 * (SByte)(object)factor);
+ product.register.sbyte_11 = (SByte)(value.register.sbyte_11 * (SByte)(object)factor);
+ product.register.sbyte_12 = (SByte)(value.register.sbyte_12 * (SByte)(object)factor);
+ product.register.sbyte_13 = (SByte)(value.register.sbyte_13 * (SByte)(object)factor);
+ product.register.sbyte_14 = (SByte)(value.register.sbyte_14 * (SByte)(object)factor);
+ product.register.sbyte_15 = (SByte)(value.register.sbyte_15 * (SByte)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ product.register.uint16_0 = (UInt16)(value.register.uint16_0 * (UInt16)(object)factor);
+ product.register.uint16_1 = (UInt16)(value.register.uint16_1 * (UInt16)(object)factor);
+ product.register.uint16_2 = (UInt16)(value.register.uint16_2 * (UInt16)(object)factor);
+ product.register.uint16_3 = (UInt16)(value.register.uint16_3 * (UInt16)(object)factor);
+ product.register.uint16_4 = (UInt16)(value.register.uint16_4 * (UInt16)(object)factor);
+ product.register.uint16_5 = (UInt16)(value.register.uint16_5 * (UInt16)(object)factor);
+ product.register.uint16_6 = (UInt16)(value.register.uint16_6 * (UInt16)(object)factor);
+ product.register.uint16_7 = (UInt16)(value.register.uint16_7 * (UInt16)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ product.register.int16_0 = (Int16)(value.register.int16_0 * (Int16)(object)factor);
+ product.register.int16_1 = (Int16)(value.register.int16_1 * (Int16)(object)factor);
+ product.register.int16_2 = (Int16)(value.register.int16_2 * (Int16)(object)factor);
+ product.register.int16_3 = (Int16)(value.register.int16_3 * (Int16)(object)factor);
+ product.register.int16_4 = (Int16)(value.register.int16_4 * (Int16)(object)factor);
+ product.register.int16_5 = (Int16)(value.register.int16_5 * (Int16)(object)factor);
+ product.register.int16_6 = (Int16)(value.register.int16_6 * (Int16)(object)factor);
+ product.register.int16_7 = (Int16)(value.register.int16_7 * (Int16)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ product.register.uint32_0 = (UInt32)(value.register.uint32_0 * (UInt32)(object)factor);
+ product.register.uint32_1 = (UInt32)(value.register.uint32_1 * (UInt32)(object)factor);
+ product.register.uint32_2 = (UInt32)(value.register.uint32_2 * (UInt32)(object)factor);
+ product.register.uint32_3 = (UInt32)(value.register.uint32_3 * (UInt32)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ product.register.int32_0 = (Int32)(value.register.int32_0 * (Int32)(object)factor);
+ product.register.int32_1 = (Int32)(value.register.int32_1 * (Int32)(object)factor);
+ product.register.int32_2 = (Int32)(value.register.int32_2 * (Int32)(object)factor);
+ product.register.int32_3 = (Int32)(value.register.int32_3 * (Int32)(object)factor);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ product.register.uint64_0 = (UInt64)(value.register.uint64_0 * (UInt64)(object)factor);
+ product.register.uint64_1 = (UInt64)(value.register.uint64_1 * (UInt64)(object)factor);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ product.register.int64_0 = (Int64)(value.register.int64_0 * (Int64)(object)factor);
+ product.register.int64_1 = (Int64)(value.register.int64_1 * (Int64)(object)factor);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ product.register.single_0 = (Single)(value.register.single_0 * (Single)(object)factor);
+ product.register.single_1 = (Single)(value.register.single_1 * (Single)(object)factor);
+ product.register.single_2 = (Single)(value.register.single_2 * (Single)(object)factor);
+ product.register.single_3 = (Single)(value.register.single_3 * (Single)(object)factor);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ product.register.double_0 = (Double)(value.register.double_0 * (Double)(object)factor);
+ product.register.double_1 = (Double)(value.register.double_1 * (Double)(object)factor);
+ }
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Divides the first vector by the second.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The vector resulting from the division.</returns>
+ public static unsafe Vector<T> operator /(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Byte)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (SByte)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt16)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int16)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt32)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int32)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (UInt64)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int64)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Single)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Double)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> quotient = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ quotient.register.byte_0 = (Byte)(left.register.byte_0 / right.register.byte_0);
+ quotient.register.byte_1 = (Byte)(left.register.byte_1 / right.register.byte_1);
+ quotient.register.byte_2 = (Byte)(left.register.byte_2 / right.register.byte_2);
+ quotient.register.byte_3 = (Byte)(left.register.byte_3 / right.register.byte_3);
+ quotient.register.byte_4 = (Byte)(left.register.byte_4 / right.register.byte_4);
+ quotient.register.byte_5 = (Byte)(left.register.byte_5 / right.register.byte_5);
+ quotient.register.byte_6 = (Byte)(left.register.byte_6 / right.register.byte_6);
+ quotient.register.byte_7 = (Byte)(left.register.byte_7 / right.register.byte_7);
+ quotient.register.byte_8 = (Byte)(left.register.byte_8 / right.register.byte_8);
+ quotient.register.byte_9 = (Byte)(left.register.byte_9 / right.register.byte_9);
+ quotient.register.byte_10 = (Byte)(left.register.byte_10 / right.register.byte_10);
+ quotient.register.byte_11 = (Byte)(left.register.byte_11 / right.register.byte_11);
+ quotient.register.byte_12 = (Byte)(left.register.byte_12 / right.register.byte_12);
+ quotient.register.byte_13 = (Byte)(left.register.byte_13 / right.register.byte_13);
+ quotient.register.byte_14 = (Byte)(left.register.byte_14 / right.register.byte_14);
+ quotient.register.byte_15 = (Byte)(left.register.byte_15 / right.register.byte_15);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ quotient.register.sbyte_0 = (SByte)(left.register.sbyte_0 / right.register.sbyte_0);
+ quotient.register.sbyte_1 = (SByte)(left.register.sbyte_1 / right.register.sbyte_1);
+ quotient.register.sbyte_2 = (SByte)(left.register.sbyte_2 / right.register.sbyte_2);
+ quotient.register.sbyte_3 = (SByte)(left.register.sbyte_3 / right.register.sbyte_3);
+ quotient.register.sbyte_4 = (SByte)(left.register.sbyte_4 / right.register.sbyte_4);
+ quotient.register.sbyte_5 = (SByte)(left.register.sbyte_5 / right.register.sbyte_5);
+ quotient.register.sbyte_6 = (SByte)(left.register.sbyte_6 / right.register.sbyte_6);
+ quotient.register.sbyte_7 = (SByte)(left.register.sbyte_7 / right.register.sbyte_7);
+ quotient.register.sbyte_8 = (SByte)(left.register.sbyte_8 / right.register.sbyte_8);
+ quotient.register.sbyte_9 = (SByte)(left.register.sbyte_9 / right.register.sbyte_9);
+ quotient.register.sbyte_10 = (SByte)(left.register.sbyte_10 / right.register.sbyte_10);
+ quotient.register.sbyte_11 = (SByte)(left.register.sbyte_11 / right.register.sbyte_11);
+ quotient.register.sbyte_12 = (SByte)(left.register.sbyte_12 / right.register.sbyte_12);
+ quotient.register.sbyte_13 = (SByte)(left.register.sbyte_13 / right.register.sbyte_13);
+ quotient.register.sbyte_14 = (SByte)(left.register.sbyte_14 / right.register.sbyte_14);
+ quotient.register.sbyte_15 = (SByte)(left.register.sbyte_15 / right.register.sbyte_15);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ quotient.register.uint16_0 = (UInt16)(left.register.uint16_0 / right.register.uint16_0);
+ quotient.register.uint16_1 = (UInt16)(left.register.uint16_1 / right.register.uint16_1);
+ quotient.register.uint16_2 = (UInt16)(left.register.uint16_2 / right.register.uint16_2);
+ quotient.register.uint16_3 = (UInt16)(left.register.uint16_3 / right.register.uint16_3);
+ quotient.register.uint16_4 = (UInt16)(left.register.uint16_4 / right.register.uint16_4);
+ quotient.register.uint16_5 = (UInt16)(left.register.uint16_5 / right.register.uint16_5);
+ quotient.register.uint16_6 = (UInt16)(left.register.uint16_6 / right.register.uint16_6);
+ quotient.register.uint16_7 = (UInt16)(left.register.uint16_7 / right.register.uint16_7);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ quotient.register.int16_0 = (Int16)(left.register.int16_0 / right.register.int16_0);
+ quotient.register.int16_1 = (Int16)(left.register.int16_1 / right.register.int16_1);
+ quotient.register.int16_2 = (Int16)(left.register.int16_2 / right.register.int16_2);
+ quotient.register.int16_3 = (Int16)(left.register.int16_3 / right.register.int16_3);
+ quotient.register.int16_4 = (Int16)(left.register.int16_4 / right.register.int16_4);
+ quotient.register.int16_5 = (Int16)(left.register.int16_5 / right.register.int16_5);
+ quotient.register.int16_6 = (Int16)(left.register.int16_6 / right.register.int16_6);
+ quotient.register.int16_7 = (Int16)(left.register.int16_7 / right.register.int16_7);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ quotient.register.uint32_0 = (UInt32)(left.register.uint32_0 / right.register.uint32_0);
+ quotient.register.uint32_1 = (UInt32)(left.register.uint32_1 / right.register.uint32_1);
+ quotient.register.uint32_2 = (UInt32)(left.register.uint32_2 / right.register.uint32_2);
+ quotient.register.uint32_3 = (UInt32)(left.register.uint32_3 / right.register.uint32_3);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ quotient.register.int32_0 = (Int32)(left.register.int32_0 / right.register.int32_0);
+ quotient.register.int32_1 = (Int32)(left.register.int32_1 / right.register.int32_1);
+ quotient.register.int32_2 = (Int32)(left.register.int32_2 / right.register.int32_2);
+ quotient.register.int32_3 = (Int32)(left.register.int32_3 / right.register.int32_3);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ quotient.register.uint64_0 = (UInt64)(left.register.uint64_0 / right.register.uint64_0);
+ quotient.register.uint64_1 = (UInt64)(left.register.uint64_1 / right.register.uint64_1);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ quotient.register.int64_0 = (Int64)(left.register.int64_0 / right.register.int64_0);
+ quotient.register.int64_1 = (Int64)(left.register.int64_1 / right.register.int64_1);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ quotient.register.single_0 = (Single)(left.register.single_0 / right.register.single_0);
+ quotient.register.single_1 = (Single)(left.register.single_1 / right.register.single_1);
+ quotient.register.single_2 = (Single)(left.register.single_2 / right.register.single_2);
+ quotient.register.single_3 = (Single)(left.register.single_3 / right.register.single_3);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ quotient.register.double_0 = (Double)(left.register.double_0 / right.register.double_0);
+ quotient.register.double_1 = (Double)(left.register.double_1 / right.register.double_1);
+ }
+ return quotient;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Negates a given vector.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The negated vector.</returns>
+ public static Vector<T> operator -(Vector<T> value)
+ {
+ return Zero - value;
+ }
+ #endregion Arithmetic Operators
+
+ #region Bitwise Operators
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-and operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator &(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] & rightBase[g];
+ }
+ }
+ else
+ {
+ result.register.int64_0 = left.register.int64_0 & right.register.int64_0;
+ result.register.int64_1 = left.register.int64_1 & right.register.int64_1;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator |(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] | rightBase[g];
+ }
+ }
+ else
+ {
+ result.register.int64_0 = left.register.int64_0 | right.register.int64_0;
+ result.register.int64_1 = left.register.int64_1 | right.register.int64_1;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-exclusive-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator ^(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] ^ rightBase[g];
+ }
+ }
+ else
+ {
+ result.register.int64_0 = left.register.int64_0 ^ right.register.int64_0;
+ result.register.int64_1 = left.register.int64_1 ^ right.register.int64_1;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are obtained by taking the one's complement of the given vector's elements.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The one's complement vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> operator ~(Vector<T> value)
+ {
+ return s_allOnes ^ value;
+ }
+ #endregion Bitwise Operators
+
+ #region Logical Operators
+ /// <summary>
+ /// Returns a boolean indicating whether each pair of elements in the given vectors are equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The first vector to compare.</param>
+ /// <returns>True if all elements are equal; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Vector<T> left, Vector<T> right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any single pair of elements in the given vectors are not equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if left and right are not equal; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Vector<T> left, Vector<T> right)
+ {
+ return !(left == right);
+ }
+ #endregion Logical Operators
+
+ #region Conversions
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Byte>(Vector<T> value)
+ {
+ return new Vector<Byte>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static explicit operator Vector<SByte>(Vector<T> value)
+ {
+ return new Vector<SByte>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static explicit operator Vector<UInt16>(Vector<T> value)
+ {
+ return new Vector<UInt16>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Int16>(Vector<T> value)
+ {
+ return new Vector<Int16>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static explicit operator Vector<UInt32>(Vector<T> value)
+ {
+ return new Vector<UInt32>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Int32>(Vector<T> value)
+ {
+ return new Vector<Int32>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static explicit operator Vector<UInt64>(Vector<T> value)
+ {
+ return new Vector<UInt64>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Int64>(Vector<T> value)
+ {
+ return new Vector<Int64>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Single>(Vector<T> value)
+ {
+ return new Vector<Single>(ref value.register);
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [Intrinsic]
+ public static explicit operator Vector<Double>(Vector<T> value)
+ {
+ return new Vector<Double>(ref value.register);
+ }
+
+ #endregion Conversions
+
+ #region Internal Comparison Methods
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> Equals(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+ if (typeof(T) == typeof(Byte))
+ {
+ register.byte_0 = left.register.byte_0 == right.register.byte_0 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_1 = left.register.byte_1 == right.register.byte_1 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_2 = left.register.byte_2 == right.register.byte_2 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_3 = left.register.byte_3 == right.register.byte_3 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_4 = left.register.byte_4 == right.register.byte_4 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_5 = left.register.byte_5 == right.register.byte_5 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_6 = left.register.byte_6 == right.register.byte_6 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_7 = left.register.byte_7 == right.register.byte_7 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_8 = left.register.byte_8 == right.register.byte_8 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_9 = left.register.byte_9 == right.register.byte_9 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_10 = left.register.byte_10 == right.register.byte_10 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_11 = left.register.byte_11 == right.register.byte_11 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_12 = left.register.byte_12 == right.register.byte_12 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_13 = left.register.byte_13 == right.register.byte_13 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_14 = left.register.byte_14 == right.register.byte_14 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_15 = left.register.byte_15 == right.register.byte_15 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ register.sbyte_0 = left.register.sbyte_0 == right.register.sbyte_0 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_1 = left.register.sbyte_1 == right.register.sbyte_1 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_2 = left.register.sbyte_2 == right.register.sbyte_2 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_3 = left.register.sbyte_3 == right.register.sbyte_3 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_4 = left.register.sbyte_4 == right.register.sbyte_4 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_5 = left.register.sbyte_5 == right.register.sbyte_5 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_6 = left.register.sbyte_6 == right.register.sbyte_6 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_7 = left.register.sbyte_7 == right.register.sbyte_7 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_8 = left.register.sbyte_8 == right.register.sbyte_8 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_9 = left.register.sbyte_9 == right.register.sbyte_9 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_10 = left.register.sbyte_10 == right.register.sbyte_10 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_11 = left.register.sbyte_11 == right.register.sbyte_11 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_12 = left.register.sbyte_12 == right.register.sbyte_12 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_13 = left.register.sbyte_13 == right.register.sbyte_13 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_14 = left.register.sbyte_14 == right.register.sbyte_14 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_15 = left.register.sbyte_15 == right.register.sbyte_15 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ register.uint16_0 = left.register.uint16_0 == right.register.uint16_0 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_1 = left.register.uint16_1 == right.register.uint16_1 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_2 = left.register.uint16_2 == right.register.uint16_2 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_3 = left.register.uint16_3 == right.register.uint16_3 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_4 = left.register.uint16_4 == right.register.uint16_4 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_5 = left.register.uint16_5 == right.register.uint16_5 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_6 = left.register.uint16_6 == right.register.uint16_6 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_7 = left.register.uint16_7 == right.register.uint16_7 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ register.int16_0 = left.register.int16_0 == right.register.int16_0 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_1 = left.register.int16_1 == right.register.int16_1 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_2 = left.register.int16_2 == right.register.int16_2 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_3 = left.register.int16_3 == right.register.int16_3 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_4 = left.register.int16_4 == right.register.int16_4 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_5 = left.register.int16_5 == right.register.int16_5 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_6 = left.register.int16_6 == right.register.int16_6 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_7 = left.register.int16_7 == right.register.int16_7 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ register.uint32_0 = left.register.uint32_0 == right.register.uint32_0 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_1 = left.register.uint32_1 == right.register.uint32_1 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_2 = left.register.uint32_2 == right.register.uint32_2 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_3 = left.register.uint32_3 == right.register.uint32_3 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ register.int32_0 = left.register.int32_0 == right.register.int32_0 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_1 = left.register.int32_1 == right.register.int32_1 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_2 = left.register.int32_2 == right.register.int32_2 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_3 = left.register.int32_3 == right.register.int32_3 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ register.uint64_0 = left.register.uint64_0 == right.register.uint64_0 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ register.uint64_1 = left.register.uint64_1 == right.register.uint64_1 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ register.int64_0 = left.register.int64_0 == right.register.int64_0 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ register.int64_1 = left.register.int64_1 == right.register.int64_1 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ register.single_0 = left.register.single_0 == right.register.single_0 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_1 = left.register.single_1 == right.register.single_1 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_2 = left.register.single_2 == right.register.single_2 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_3 = left.register.single_3 == right.register.single_3 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ register.double_0 = left.register.double_0 == right.register.double_0 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ register.double_1 = left.register.double_1 == right.register.double_1 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ return new Vector<T>(ref register);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> LessThan(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+ if (typeof(T) == typeof(Byte))
+ {
+ register.byte_0 = left.register.byte_0 < right.register.byte_0 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_1 = left.register.byte_1 < right.register.byte_1 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_2 = left.register.byte_2 < right.register.byte_2 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_3 = left.register.byte_3 < right.register.byte_3 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_4 = left.register.byte_4 < right.register.byte_4 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_5 = left.register.byte_5 < right.register.byte_5 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_6 = left.register.byte_6 < right.register.byte_6 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_7 = left.register.byte_7 < right.register.byte_7 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_8 = left.register.byte_8 < right.register.byte_8 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_9 = left.register.byte_9 < right.register.byte_9 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_10 = left.register.byte_10 < right.register.byte_10 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_11 = left.register.byte_11 < right.register.byte_11 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_12 = left.register.byte_12 < right.register.byte_12 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_13 = left.register.byte_13 < right.register.byte_13 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_14 = left.register.byte_14 < right.register.byte_14 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_15 = left.register.byte_15 < right.register.byte_15 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ register.sbyte_0 = left.register.sbyte_0 < right.register.sbyte_0 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_1 = left.register.sbyte_1 < right.register.sbyte_1 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_2 = left.register.sbyte_2 < right.register.sbyte_2 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_3 = left.register.sbyte_3 < right.register.sbyte_3 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_4 = left.register.sbyte_4 < right.register.sbyte_4 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_5 = left.register.sbyte_5 < right.register.sbyte_5 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_6 = left.register.sbyte_6 < right.register.sbyte_6 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_7 = left.register.sbyte_7 < right.register.sbyte_7 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_8 = left.register.sbyte_8 < right.register.sbyte_8 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_9 = left.register.sbyte_9 < right.register.sbyte_9 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_10 = left.register.sbyte_10 < right.register.sbyte_10 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_11 = left.register.sbyte_11 < right.register.sbyte_11 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_12 = left.register.sbyte_12 < right.register.sbyte_12 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_13 = left.register.sbyte_13 < right.register.sbyte_13 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_14 = left.register.sbyte_14 < right.register.sbyte_14 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_15 = left.register.sbyte_15 < right.register.sbyte_15 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ register.uint16_0 = left.register.uint16_0 < right.register.uint16_0 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_1 = left.register.uint16_1 < right.register.uint16_1 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_2 = left.register.uint16_2 < right.register.uint16_2 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_3 = left.register.uint16_3 < right.register.uint16_3 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_4 = left.register.uint16_4 < right.register.uint16_4 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_5 = left.register.uint16_5 < right.register.uint16_5 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_6 = left.register.uint16_6 < right.register.uint16_6 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_7 = left.register.uint16_7 < right.register.uint16_7 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ register.int16_0 = left.register.int16_0 < right.register.int16_0 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_1 = left.register.int16_1 < right.register.int16_1 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_2 = left.register.int16_2 < right.register.int16_2 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_3 = left.register.int16_3 < right.register.int16_3 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_4 = left.register.int16_4 < right.register.int16_4 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_5 = left.register.int16_5 < right.register.int16_5 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_6 = left.register.int16_6 < right.register.int16_6 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_7 = left.register.int16_7 < right.register.int16_7 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ register.uint32_0 = left.register.uint32_0 < right.register.uint32_0 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_1 = left.register.uint32_1 < right.register.uint32_1 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_2 = left.register.uint32_2 < right.register.uint32_2 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_3 = left.register.uint32_3 < right.register.uint32_3 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ register.int32_0 = left.register.int32_0 < right.register.int32_0 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_1 = left.register.int32_1 < right.register.int32_1 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_2 = left.register.int32_2 < right.register.int32_2 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_3 = left.register.int32_3 < right.register.int32_3 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ register.uint64_0 = left.register.uint64_0 < right.register.uint64_0 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ register.uint64_1 = left.register.uint64_1 < right.register.uint64_1 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ register.int64_0 = left.register.int64_0 < right.register.int64_0 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ register.int64_1 = left.register.int64_1 < right.register.int64_1 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ register.single_0 = left.register.single_0 < right.register.single_0 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_1 = left.register.single_1 < right.register.single_1 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_2 = left.register.single_2 < right.register.single_2 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_3 = left.register.single_3 < right.register.single_3 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ register.double_0 = left.register.double_0 < right.register.double_0 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ register.double_1 = left.register.double_1 < right.register.double_1 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ return new Vector<T>(ref register);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> GreaterThan(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+ if (typeof(T) == typeof(Byte))
+ {
+ register.byte_0 = left.register.byte_0 > right.register.byte_0 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_1 = left.register.byte_1 > right.register.byte_1 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_2 = left.register.byte_2 > right.register.byte_2 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_3 = left.register.byte_3 > right.register.byte_3 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_4 = left.register.byte_4 > right.register.byte_4 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_5 = left.register.byte_5 > right.register.byte_5 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_6 = left.register.byte_6 > right.register.byte_6 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_7 = left.register.byte_7 > right.register.byte_7 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_8 = left.register.byte_8 > right.register.byte_8 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_9 = left.register.byte_9 > right.register.byte_9 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_10 = left.register.byte_10 > right.register.byte_10 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_11 = left.register.byte_11 > right.register.byte_11 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_12 = left.register.byte_12 > right.register.byte_12 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_13 = left.register.byte_13 > right.register.byte_13 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_14 = left.register.byte_14 > right.register.byte_14 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ register.byte_15 = left.register.byte_15 > right.register.byte_15 ? ConstantHelper.GetByteWithAllBitsSet() : (Byte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ register.sbyte_0 = left.register.sbyte_0 > right.register.sbyte_0 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_1 = left.register.sbyte_1 > right.register.sbyte_1 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_2 = left.register.sbyte_2 > right.register.sbyte_2 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_3 = left.register.sbyte_3 > right.register.sbyte_3 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_4 = left.register.sbyte_4 > right.register.sbyte_4 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_5 = left.register.sbyte_5 > right.register.sbyte_5 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_6 = left.register.sbyte_6 > right.register.sbyte_6 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_7 = left.register.sbyte_7 > right.register.sbyte_7 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_8 = left.register.sbyte_8 > right.register.sbyte_8 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_9 = left.register.sbyte_9 > right.register.sbyte_9 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_10 = left.register.sbyte_10 > right.register.sbyte_10 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_11 = left.register.sbyte_11 > right.register.sbyte_11 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_12 = left.register.sbyte_12 > right.register.sbyte_12 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_13 = left.register.sbyte_13 > right.register.sbyte_13 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_14 = left.register.sbyte_14 > right.register.sbyte_14 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ register.sbyte_15 = left.register.sbyte_15 > right.register.sbyte_15 ? ConstantHelper.GetSByteWithAllBitsSet() : (SByte)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ register.uint16_0 = left.register.uint16_0 > right.register.uint16_0 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_1 = left.register.uint16_1 > right.register.uint16_1 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_2 = left.register.uint16_2 > right.register.uint16_2 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_3 = left.register.uint16_3 > right.register.uint16_3 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_4 = left.register.uint16_4 > right.register.uint16_4 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_5 = left.register.uint16_5 > right.register.uint16_5 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_6 = left.register.uint16_6 > right.register.uint16_6 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ register.uint16_7 = left.register.uint16_7 > right.register.uint16_7 ? ConstantHelper.GetUInt16WithAllBitsSet() : (UInt16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ register.int16_0 = left.register.int16_0 > right.register.int16_0 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_1 = left.register.int16_1 > right.register.int16_1 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_2 = left.register.int16_2 > right.register.int16_2 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_3 = left.register.int16_3 > right.register.int16_3 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_4 = left.register.int16_4 > right.register.int16_4 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_5 = left.register.int16_5 > right.register.int16_5 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_6 = left.register.int16_6 > right.register.int16_6 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ register.int16_7 = left.register.int16_7 > right.register.int16_7 ? ConstantHelper.GetInt16WithAllBitsSet() : (Int16)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ register.uint32_0 = left.register.uint32_0 > right.register.uint32_0 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_1 = left.register.uint32_1 > right.register.uint32_1 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_2 = left.register.uint32_2 > right.register.uint32_2 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ register.uint32_3 = left.register.uint32_3 > right.register.uint32_3 ? ConstantHelper.GetUInt32WithAllBitsSet() : (UInt32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ register.int32_0 = left.register.int32_0 > right.register.int32_0 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_1 = left.register.int32_1 > right.register.int32_1 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_2 = left.register.int32_2 > right.register.int32_2 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ register.int32_3 = left.register.int32_3 > right.register.int32_3 ? ConstantHelper.GetInt32WithAllBitsSet() : (Int32)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ register.uint64_0 = left.register.uint64_0 > right.register.uint64_0 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ register.uint64_1 = left.register.uint64_1 > right.register.uint64_1 ? ConstantHelper.GetUInt64WithAllBitsSet() : (UInt64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ register.int64_0 = left.register.int64_0 > right.register.int64_0 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ register.int64_1 = left.register.int64_1 > right.register.int64_1 ? ConstantHelper.GetInt64WithAllBitsSet() : (Int64)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ register.single_0 = left.register.single_0 > right.register.single_0 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_1 = left.register.single_1 > right.register.single_1 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_2 = left.register.single_2 > right.register.single_2 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ register.single_3 = left.register.single_3 > right.register.single_3 ? ConstantHelper.GetSingleWithAllBitsSet() : (Single)0;
+ return new Vector<T>(ref register);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ register.double_0 = left.register.double_0 > right.register.double_0 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ register.double_1 = left.register.double_1 > right.register.double_1 ? ConstantHelper.GetDoubleWithAllBitsSet() : (Double)0;
+ return new Vector<T>(ref register);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static Vector<T> GreaterThanOrEqual(Vector<T> left, Vector<T> right)
+ {
+ return Equals(left, right) | GreaterThan(left, right);
+ }
+
+ [Intrinsic]
+ internal static Vector<T> LessThanOrEqual(Vector<T> left, Vector<T> right)
+ {
+ return Equals(left, right) | LessThan(left, right);
+ }
+
+ [Intrinsic]
+ internal static Vector<T> ConditionalSelect(Vector<T> condition, Vector<T> left, Vector<T> right)
+ {
+ return (left & condition) | (Vector.AndNot(right, condition));
+ }
+ #endregion Comparison Methods
+
+ #region Internal Math Methods
+ [Intrinsic]
+ internal static unsafe Vector<T> Abs(Vector<T> value)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return value;
+ }
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (SByte)(object)(Math.Abs((SByte)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int16)(object)(Math.Abs((Int16)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int32)(object)(Math.Abs((Int32)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Int64)(object)(Math.Abs((Int64)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Single)(object)(Math.Abs((Single)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (Double)(object)(Math.Abs((Double)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(SByte))
+ {
+ value.register.sbyte_0 = (SByte)(Math.Abs(value.register.sbyte_0));
+ value.register.sbyte_1 = (SByte)(Math.Abs(value.register.sbyte_1));
+ value.register.sbyte_2 = (SByte)(Math.Abs(value.register.sbyte_2));
+ value.register.sbyte_3 = (SByte)(Math.Abs(value.register.sbyte_3));
+ value.register.sbyte_4 = (SByte)(Math.Abs(value.register.sbyte_4));
+ value.register.sbyte_5 = (SByte)(Math.Abs(value.register.sbyte_5));
+ value.register.sbyte_6 = (SByte)(Math.Abs(value.register.sbyte_6));
+ value.register.sbyte_7 = (SByte)(Math.Abs(value.register.sbyte_7));
+ value.register.sbyte_8 = (SByte)(Math.Abs(value.register.sbyte_8));
+ value.register.sbyte_9 = (SByte)(Math.Abs(value.register.sbyte_9));
+ value.register.sbyte_10 = (SByte)(Math.Abs(value.register.sbyte_10));
+ value.register.sbyte_11 = (SByte)(Math.Abs(value.register.sbyte_11));
+ value.register.sbyte_12 = (SByte)(Math.Abs(value.register.sbyte_12));
+ value.register.sbyte_13 = (SByte)(Math.Abs(value.register.sbyte_13));
+ value.register.sbyte_14 = (SByte)(Math.Abs(value.register.sbyte_14));
+ value.register.sbyte_15 = (SByte)(Math.Abs(value.register.sbyte_15));
+ return value;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ value.register.int16_0 = (Int16)(Math.Abs(value.register.int16_0));
+ value.register.int16_1 = (Int16)(Math.Abs(value.register.int16_1));
+ value.register.int16_2 = (Int16)(Math.Abs(value.register.int16_2));
+ value.register.int16_3 = (Int16)(Math.Abs(value.register.int16_3));
+ value.register.int16_4 = (Int16)(Math.Abs(value.register.int16_4));
+ value.register.int16_5 = (Int16)(Math.Abs(value.register.int16_5));
+ value.register.int16_6 = (Int16)(Math.Abs(value.register.int16_6));
+ value.register.int16_7 = (Int16)(Math.Abs(value.register.int16_7));
+ return value;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ value.register.int32_0 = (Int32)(Math.Abs(value.register.int32_0));
+ value.register.int32_1 = (Int32)(Math.Abs(value.register.int32_1));
+ value.register.int32_2 = (Int32)(Math.Abs(value.register.int32_2));
+ value.register.int32_3 = (Int32)(Math.Abs(value.register.int32_3));
+ return value;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ value.register.int64_0 = (Int64)(Math.Abs(value.register.int64_0));
+ value.register.int64_1 = (Int64)(Math.Abs(value.register.int64_1));
+ return value;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ value.register.single_0 = (Single)(Math.Abs(value.register.single_0));
+ value.register.single_1 = (Single)(Math.Abs(value.register.single_1));
+ value.register.single_2 = (Single)(Math.Abs(value.register.single_2));
+ value.register.single_3 = (Single)(Math.Abs(value.register.single_3));
+ return value;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ value.register.double_0 = (Double)(Math.Abs(value.register.double_0));
+ value.register.double_1 = (Double)(Math.Abs(value.register.double_1));
+ return value;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> Min(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Byte)(object)left[g] : (Byte)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (SByte)(object)left[g] : (SByte)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (UInt16)(object)left[g] : (UInt16)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Int16)(object)left[g] : (Int16)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (UInt32)(object)left[g] : (UInt32)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Int32)(object)left[g] : (Int32)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (UInt64)(object)left[g] : (UInt64)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Int64)(object)left[g] : (Int64)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Single)(object)left[g] : (Single)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (Double)(object)left[g] : (Double)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> vec = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ vec.register.byte_0 = left.register.byte_0 < right.register.byte_0 ? left.register.byte_0 : right.register.byte_0;
+ vec.register.byte_1 = left.register.byte_1 < right.register.byte_1 ? left.register.byte_1 : right.register.byte_1;
+ vec.register.byte_2 = left.register.byte_2 < right.register.byte_2 ? left.register.byte_2 : right.register.byte_2;
+ vec.register.byte_3 = left.register.byte_3 < right.register.byte_3 ? left.register.byte_3 : right.register.byte_3;
+ vec.register.byte_4 = left.register.byte_4 < right.register.byte_4 ? left.register.byte_4 : right.register.byte_4;
+ vec.register.byte_5 = left.register.byte_5 < right.register.byte_5 ? left.register.byte_5 : right.register.byte_5;
+ vec.register.byte_6 = left.register.byte_6 < right.register.byte_6 ? left.register.byte_6 : right.register.byte_6;
+ vec.register.byte_7 = left.register.byte_7 < right.register.byte_7 ? left.register.byte_7 : right.register.byte_7;
+ vec.register.byte_8 = left.register.byte_8 < right.register.byte_8 ? left.register.byte_8 : right.register.byte_8;
+ vec.register.byte_9 = left.register.byte_9 < right.register.byte_9 ? left.register.byte_9 : right.register.byte_9;
+ vec.register.byte_10 = left.register.byte_10 < right.register.byte_10 ? left.register.byte_10 : right.register.byte_10;
+ vec.register.byte_11 = left.register.byte_11 < right.register.byte_11 ? left.register.byte_11 : right.register.byte_11;
+ vec.register.byte_12 = left.register.byte_12 < right.register.byte_12 ? left.register.byte_12 : right.register.byte_12;
+ vec.register.byte_13 = left.register.byte_13 < right.register.byte_13 ? left.register.byte_13 : right.register.byte_13;
+ vec.register.byte_14 = left.register.byte_14 < right.register.byte_14 ? left.register.byte_14 : right.register.byte_14;
+ vec.register.byte_15 = left.register.byte_15 < right.register.byte_15 ? left.register.byte_15 : right.register.byte_15;
+ return vec;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ vec.register.sbyte_0 = left.register.sbyte_0 < right.register.sbyte_0 ? left.register.sbyte_0 : right.register.sbyte_0;
+ vec.register.sbyte_1 = left.register.sbyte_1 < right.register.sbyte_1 ? left.register.sbyte_1 : right.register.sbyte_1;
+ vec.register.sbyte_2 = left.register.sbyte_2 < right.register.sbyte_2 ? left.register.sbyte_2 : right.register.sbyte_2;
+ vec.register.sbyte_3 = left.register.sbyte_3 < right.register.sbyte_3 ? left.register.sbyte_3 : right.register.sbyte_3;
+ vec.register.sbyte_4 = left.register.sbyte_4 < right.register.sbyte_4 ? left.register.sbyte_4 : right.register.sbyte_4;
+ vec.register.sbyte_5 = left.register.sbyte_5 < right.register.sbyte_5 ? left.register.sbyte_5 : right.register.sbyte_5;
+ vec.register.sbyte_6 = left.register.sbyte_6 < right.register.sbyte_6 ? left.register.sbyte_6 : right.register.sbyte_6;
+ vec.register.sbyte_7 = left.register.sbyte_7 < right.register.sbyte_7 ? left.register.sbyte_7 : right.register.sbyte_7;
+ vec.register.sbyte_8 = left.register.sbyte_8 < right.register.sbyte_8 ? left.register.sbyte_8 : right.register.sbyte_8;
+ vec.register.sbyte_9 = left.register.sbyte_9 < right.register.sbyte_9 ? left.register.sbyte_9 : right.register.sbyte_9;
+ vec.register.sbyte_10 = left.register.sbyte_10 < right.register.sbyte_10 ? left.register.sbyte_10 : right.register.sbyte_10;
+ vec.register.sbyte_11 = left.register.sbyte_11 < right.register.sbyte_11 ? left.register.sbyte_11 : right.register.sbyte_11;
+ vec.register.sbyte_12 = left.register.sbyte_12 < right.register.sbyte_12 ? left.register.sbyte_12 : right.register.sbyte_12;
+ vec.register.sbyte_13 = left.register.sbyte_13 < right.register.sbyte_13 ? left.register.sbyte_13 : right.register.sbyte_13;
+ vec.register.sbyte_14 = left.register.sbyte_14 < right.register.sbyte_14 ? left.register.sbyte_14 : right.register.sbyte_14;
+ vec.register.sbyte_15 = left.register.sbyte_15 < right.register.sbyte_15 ? left.register.sbyte_15 : right.register.sbyte_15;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ vec.register.uint16_0 = left.register.uint16_0 < right.register.uint16_0 ? left.register.uint16_0 : right.register.uint16_0;
+ vec.register.uint16_1 = left.register.uint16_1 < right.register.uint16_1 ? left.register.uint16_1 : right.register.uint16_1;
+ vec.register.uint16_2 = left.register.uint16_2 < right.register.uint16_2 ? left.register.uint16_2 : right.register.uint16_2;
+ vec.register.uint16_3 = left.register.uint16_3 < right.register.uint16_3 ? left.register.uint16_3 : right.register.uint16_3;
+ vec.register.uint16_4 = left.register.uint16_4 < right.register.uint16_4 ? left.register.uint16_4 : right.register.uint16_4;
+ vec.register.uint16_5 = left.register.uint16_5 < right.register.uint16_5 ? left.register.uint16_5 : right.register.uint16_5;
+ vec.register.uint16_6 = left.register.uint16_6 < right.register.uint16_6 ? left.register.uint16_6 : right.register.uint16_6;
+ vec.register.uint16_7 = left.register.uint16_7 < right.register.uint16_7 ? left.register.uint16_7 : right.register.uint16_7;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ vec.register.int16_0 = left.register.int16_0 < right.register.int16_0 ? left.register.int16_0 : right.register.int16_0;
+ vec.register.int16_1 = left.register.int16_1 < right.register.int16_1 ? left.register.int16_1 : right.register.int16_1;
+ vec.register.int16_2 = left.register.int16_2 < right.register.int16_2 ? left.register.int16_2 : right.register.int16_2;
+ vec.register.int16_3 = left.register.int16_3 < right.register.int16_3 ? left.register.int16_3 : right.register.int16_3;
+ vec.register.int16_4 = left.register.int16_4 < right.register.int16_4 ? left.register.int16_4 : right.register.int16_4;
+ vec.register.int16_5 = left.register.int16_5 < right.register.int16_5 ? left.register.int16_5 : right.register.int16_5;
+ vec.register.int16_6 = left.register.int16_6 < right.register.int16_6 ? left.register.int16_6 : right.register.int16_6;
+ vec.register.int16_7 = left.register.int16_7 < right.register.int16_7 ? left.register.int16_7 : right.register.int16_7;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ vec.register.uint32_0 = left.register.uint32_0 < right.register.uint32_0 ? left.register.uint32_0 : right.register.uint32_0;
+ vec.register.uint32_1 = left.register.uint32_1 < right.register.uint32_1 ? left.register.uint32_1 : right.register.uint32_1;
+ vec.register.uint32_2 = left.register.uint32_2 < right.register.uint32_2 ? left.register.uint32_2 : right.register.uint32_2;
+ vec.register.uint32_3 = left.register.uint32_3 < right.register.uint32_3 ? left.register.uint32_3 : right.register.uint32_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ vec.register.int32_0 = left.register.int32_0 < right.register.int32_0 ? left.register.int32_0 : right.register.int32_0;
+ vec.register.int32_1 = left.register.int32_1 < right.register.int32_1 ? left.register.int32_1 : right.register.int32_1;
+ vec.register.int32_2 = left.register.int32_2 < right.register.int32_2 ? left.register.int32_2 : right.register.int32_2;
+ vec.register.int32_3 = left.register.int32_3 < right.register.int32_3 ? left.register.int32_3 : right.register.int32_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ vec.register.uint64_0 = left.register.uint64_0 < right.register.uint64_0 ? left.register.uint64_0 : right.register.uint64_0;
+ vec.register.uint64_1 = left.register.uint64_1 < right.register.uint64_1 ? left.register.uint64_1 : right.register.uint64_1;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ vec.register.int64_0 = left.register.int64_0 < right.register.int64_0 ? left.register.int64_0 : right.register.int64_0;
+ vec.register.int64_1 = left.register.int64_1 < right.register.int64_1 ? left.register.int64_1 : right.register.int64_1;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ vec.register.single_0 = left.register.single_0 < right.register.single_0 ? left.register.single_0 : right.register.single_0;
+ vec.register.single_1 = left.register.single_1 < right.register.single_1 ? left.register.single_1 : right.register.single_1;
+ vec.register.single_2 = left.register.single_2 < right.register.single_2 ? left.register.single_2 : right.register.single_2;
+ vec.register.single_3 = left.register.single_3 < right.register.single_3 ? left.register.single_3 : right.register.single_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ vec.register.double_0 = left.register.double_0 < right.register.double_0 ? left.register.double_0 : right.register.double_0;
+ vec.register.double_1 = left.register.double_1 < right.register.double_1 ? left.register.double_1 : right.register.double_1;
+ return vec;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> Max(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Byte)(object)left[g] : (Byte)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (SByte)(object)left[g] : (SByte)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (UInt16)(object)left[g] : (UInt16)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Int16)(object)left[g] : (Int16)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (UInt32)(object)left[g] : (UInt32)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Int32)(object)left[g] : (Int32)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (UInt64)(object)left[g] : (UInt64)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Int64)(object)left[g] : (Int64)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Single)(object)left[g] : (Single)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (Double)(object)left[g] : (Double)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> vec = new Vector<T>();
+ if (typeof(T) == typeof(Byte))
+ {
+ vec.register.byte_0 = left.register.byte_0 > right.register.byte_0 ? left.register.byte_0 : right.register.byte_0;
+ vec.register.byte_1 = left.register.byte_1 > right.register.byte_1 ? left.register.byte_1 : right.register.byte_1;
+ vec.register.byte_2 = left.register.byte_2 > right.register.byte_2 ? left.register.byte_2 : right.register.byte_2;
+ vec.register.byte_3 = left.register.byte_3 > right.register.byte_3 ? left.register.byte_3 : right.register.byte_3;
+ vec.register.byte_4 = left.register.byte_4 > right.register.byte_4 ? left.register.byte_4 : right.register.byte_4;
+ vec.register.byte_5 = left.register.byte_5 > right.register.byte_5 ? left.register.byte_5 : right.register.byte_5;
+ vec.register.byte_6 = left.register.byte_6 > right.register.byte_6 ? left.register.byte_6 : right.register.byte_6;
+ vec.register.byte_7 = left.register.byte_7 > right.register.byte_7 ? left.register.byte_7 : right.register.byte_7;
+ vec.register.byte_8 = left.register.byte_8 > right.register.byte_8 ? left.register.byte_8 : right.register.byte_8;
+ vec.register.byte_9 = left.register.byte_9 > right.register.byte_9 ? left.register.byte_9 : right.register.byte_9;
+ vec.register.byte_10 = left.register.byte_10 > right.register.byte_10 ? left.register.byte_10 : right.register.byte_10;
+ vec.register.byte_11 = left.register.byte_11 > right.register.byte_11 ? left.register.byte_11 : right.register.byte_11;
+ vec.register.byte_12 = left.register.byte_12 > right.register.byte_12 ? left.register.byte_12 : right.register.byte_12;
+ vec.register.byte_13 = left.register.byte_13 > right.register.byte_13 ? left.register.byte_13 : right.register.byte_13;
+ vec.register.byte_14 = left.register.byte_14 > right.register.byte_14 ? left.register.byte_14 : right.register.byte_14;
+ vec.register.byte_15 = left.register.byte_15 > right.register.byte_15 ? left.register.byte_15 : right.register.byte_15;
+ return vec;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ vec.register.sbyte_0 = left.register.sbyte_0 > right.register.sbyte_0 ? left.register.sbyte_0 : right.register.sbyte_0;
+ vec.register.sbyte_1 = left.register.sbyte_1 > right.register.sbyte_1 ? left.register.sbyte_1 : right.register.sbyte_1;
+ vec.register.sbyte_2 = left.register.sbyte_2 > right.register.sbyte_2 ? left.register.sbyte_2 : right.register.sbyte_2;
+ vec.register.sbyte_3 = left.register.sbyte_3 > right.register.sbyte_3 ? left.register.sbyte_3 : right.register.sbyte_3;
+ vec.register.sbyte_4 = left.register.sbyte_4 > right.register.sbyte_4 ? left.register.sbyte_4 : right.register.sbyte_4;
+ vec.register.sbyte_5 = left.register.sbyte_5 > right.register.sbyte_5 ? left.register.sbyte_5 : right.register.sbyte_5;
+ vec.register.sbyte_6 = left.register.sbyte_6 > right.register.sbyte_6 ? left.register.sbyte_6 : right.register.sbyte_6;
+ vec.register.sbyte_7 = left.register.sbyte_7 > right.register.sbyte_7 ? left.register.sbyte_7 : right.register.sbyte_7;
+ vec.register.sbyte_8 = left.register.sbyte_8 > right.register.sbyte_8 ? left.register.sbyte_8 : right.register.sbyte_8;
+ vec.register.sbyte_9 = left.register.sbyte_9 > right.register.sbyte_9 ? left.register.sbyte_9 : right.register.sbyte_9;
+ vec.register.sbyte_10 = left.register.sbyte_10 > right.register.sbyte_10 ? left.register.sbyte_10 : right.register.sbyte_10;
+ vec.register.sbyte_11 = left.register.sbyte_11 > right.register.sbyte_11 ? left.register.sbyte_11 : right.register.sbyte_11;
+ vec.register.sbyte_12 = left.register.sbyte_12 > right.register.sbyte_12 ? left.register.sbyte_12 : right.register.sbyte_12;
+ vec.register.sbyte_13 = left.register.sbyte_13 > right.register.sbyte_13 ? left.register.sbyte_13 : right.register.sbyte_13;
+ vec.register.sbyte_14 = left.register.sbyte_14 > right.register.sbyte_14 ? left.register.sbyte_14 : right.register.sbyte_14;
+ vec.register.sbyte_15 = left.register.sbyte_15 > right.register.sbyte_15 ? left.register.sbyte_15 : right.register.sbyte_15;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ vec.register.uint16_0 = left.register.uint16_0 > right.register.uint16_0 ? left.register.uint16_0 : right.register.uint16_0;
+ vec.register.uint16_1 = left.register.uint16_1 > right.register.uint16_1 ? left.register.uint16_1 : right.register.uint16_1;
+ vec.register.uint16_2 = left.register.uint16_2 > right.register.uint16_2 ? left.register.uint16_2 : right.register.uint16_2;
+ vec.register.uint16_3 = left.register.uint16_3 > right.register.uint16_3 ? left.register.uint16_3 : right.register.uint16_3;
+ vec.register.uint16_4 = left.register.uint16_4 > right.register.uint16_4 ? left.register.uint16_4 : right.register.uint16_4;
+ vec.register.uint16_5 = left.register.uint16_5 > right.register.uint16_5 ? left.register.uint16_5 : right.register.uint16_5;
+ vec.register.uint16_6 = left.register.uint16_6 > right.register.uint16_6 ? left.register.uint16_6 : right.register.uint16_6;
+ vec.register.uint16_7 = left.register.uint16_7 > right.register.uint16_7 ? left.register.uint16_7 : right.register.uint16_7;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ vec.register.int16_0 = left.register.int16_0 > right.register.int16_0 ? left.register.int16_0 : right.register.int16_0;
+ vec.register.int16_1 = left.register.int16_1 > right.register.int16_1 ? left.register.int16_1 : right.register.int16_1;
+ vec.register.int16_2 = left.register.int16_2 > right.register.int16_2 ? left.register.int16_2 : right.register.int16_2;
+ vec.register.int16_3 = left.register.int16_3 > right.register.int16_3 ? left.register.int16_3 : right.register.int16_3;
+ vec.register.int16_4 = left.register.int16_4 > right.register.int16_4 ? left.register.int16_4 : right.register.int16_4;
+ vec.register.int16_5 = left.register.int16_5 > right.register.int16_5 ? left.register.int16_5 : right.register.int16_5;
+ vec.register.int16_6 = left.register.int16_6 > right.register.int16_6 ? left.register.int16_6 : right.register.int16_6;
+ vec.register.int16_7 = left.register.int16_7 > right.register.int16_7 ? left.register.int16_7 : right.register.int16_7;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ vec.register.uint32_0 = left.register.uint32_0 > right.register.uint32_0 ? left.register.uint32_0 : right.register.uint32_0;
+ vec.register.uint32_1 = left.register.uint32_1 > right.register.uint32_1 ? left.register.uint32_1 : right.register.uint32_1;
+ vec.register.uint32_2 = left.register.uint32_2 > right.register.uint32_2 ? left.register.uint32_2 : right.register.uint32_2;
+ vec.register.uint32_3 = left.register.uint32_3 > right.register.uint32_3 ? left.register.uint32_3 : right.register.uint32_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ vec.register.int32_0 = left.register.int32_0 > right.register.int32_0 ? left.register.int32_0 : right.register.int32_0;
+ vec.register.int32_1 = left.register.int32_1 > right.register.int32_1 ? left.register.int32_1 : right.register.int32_1;
+ vec.register.int32_2 = left.register.int32_2 > right.register.int32_2 ? left.register.int32_2 : right.register.int32_2;
+ vec.register.int32_3 = left.register.int32_3 > right.register.int32_3 ? left.register.int32_3 : right.register.int32_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ vec.register.uint64_0 = left.register.uint64_0 > right.register.uint64_0 ? left.register.uint64_0 : right.register.uint64_0;
+ vec.register.uint64_1 = left.register.uint64_1 > right.register.uint64_1 ? left.register.uint64_1 : right.register.uint64_1;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ vec.register.int64_0 = left.register.int64_0 > right.register.int64_0 ? left.register.int64_0 : right.register.int64_0;
+ vec.register.int64_1 = left.register.int64_1 > right.register.int64_1 ? left.register.int64_1 : right.register.int64_1;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ vec.register.single_0 = left.register.single_0 > right.register.single_0 ? left.register.single_0 : right.register.single_0;
+ vec.register.single_1 = left.register.single_1 > right.register.single_1 ? left.register.single_1 : right.register.single_1;
+ vec.register.single_2 = left.register.single_2 > right.register.single_2 ? left.register.single_2 : right.register.single_2;
+ vec.register.single_3 = left.register.single_3 > right.register.single_3 ? left.register.single_3 : right.register.single_3;
+ return vec;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ vec.register.double_0 = left.register.double_0 > right.register.double_0 ? left.register.double_0 : right.register.double_0;
+ vec.register.double_1 = left.register.double_1 > right.register.double_1 ? left.register.double_1 : right.register.double_1;
+ return vec;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static T DotProduct(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ T product = default;
+ for (int g = 0; g < Count; g++)
+ {
+ product = ScalarAdd(product, ScalarMultiply(left[g], right[g]));
+ }
+ return product;
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte product = 0;
+ product += (Byte)(left.register.byte_0 * right.register.byte_0);
+ product += (Byte)(left.register.byte_1 * right.register.byte_1);
+ product += (Byte)(left.register.byte_2 * right.register.byte_2);
+ product += (Byte)(left.register.byte_3 * right.register.byte_3);
+ product += (Byte)(left.register.byte_4 * right.register.byte_4);
+ product += (Byte)(left.register.byte_5 * right.register.byte_5);
+ product += (Byte)(left.register.byte_6 * right.register.byte_6);
+ product += (Byte)(left.register.byte_7 * right.register.byte_7);
+ product += (Byte)(left.register.byte_8 * right.register.byte_8);
+ product += (Byte)(left.register.byte_9 * right.register.byte_9);
+ product += (Byte)(left.register.byte_10 * right.register.byte_10);
+ product += (Byte)(left.register.byte_11 * right.register.byte_11);
+ product += (Byte)(left.register.byte_12 * right.register.byte_12);
+ product += (Byte)(left.register.byte_13 * right.register.byte_13);
+ product += (Byte)(left.register.byte_14 * right.register.byte_14);
+ product += (Byte)(left.register.byte_15 * right.register.byte_15);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte product = 0;
+ product += (SByte)(left.register.sbyte_0 * right.register.sbyte_0);
+ product += (SByte)(left.register.sbyte_1 * right.register.sbyte_1);
+ product += (SByte)(left.register.sbyte_2 * right.register.sbyte_2);
+ product += (SByte)(left.register.sbyte_3 * right.register.sbyte_3);
+ product += (SByte)(left.register.sbyte_4 * right.register.sbyte_4);
+ product += (SByte)(left.register.sbyte_5 * right.register.sbyte_5);
+ product += (SByte)(left.register.sbyte_6 * right.register.sbyte_6);
+ product += (SByte)(left.register.sbyte_7 * right.register.sbyte_7);
+ product += (SByte)(left.register.sbyte_8 * right.register.sbyte_8);
+ product += (SByte)(left.register.sbyte_9 * right.register.sbyte_9);
+ product += (SByte)(left.register.sbyte_10 * right.register.sbyte_10);
+ product += (SByte)(left.register.sbyte_11 * right.register.sbyte_11);
+ product += (SByte)(left.register.sbyte_12 * right.register.sbyte_12);
+ product += (SByte)(left.register.sbyte_13 * right.register.sbyte_13);
+ product += (SByte)(left.register.sbyte_14 * right.register.sbyte_14);
+ product += (SByte)(left.register.sbyte_15 * right.register.sbyte_15);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16 product = 0;
+ product += (UInt16)(left.register.uint16_0 * right.register.uint16_0);
+ product += (UInt16)(left.register.uint16_1 * right.register.uint16_1);
+ product += (UInt16)(left.register.uint16_2 * right.register.uint16_2);
+ product += (UInt16)(left.register.uint16_3 * right.register.uint16_3);
+ product += (UInt16)(left.register.uint16_4 * right.register.uint16_4);
+ product += (UInt16)(left.register.uint16_5 * right.register.uint16_5);
+ product += (UInt16)(left.register.uint16_6 * right.register.uint16_6);
+ product += (UInt16)(left.register.uint16_7 * right.register.uint16_7);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16 product = 0;
+ product += (Int16)(left.register.int16_0 * right.register.int16_0);
+ product += (Int16)(left.register.int16_1 * right.register.int16_1);
+ product += (Int16)(left.register.int16_2 * right.register.int16_2);
+ product += (Int16)(left.register.int16_3 * right.register.int16_3);
+ product += (Int16)(left.register.int16_4 * right.register.int16_4);
+ product += (Int16)(left.register.int16_5 * right.register.int16_5);
+ product += (Int16)(left.register.int16_6 * right.register.int16_6);
+ product += (Int16)(left.register.int16_7 * right.register.int16_7);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32 product = 0;
+ product += (UInt32)(left.register.uint32_0 * right.register.uint32_0);
+ product += (UInt32)(left.register.uint32_1 * right.register.uint32_1);
+ product += (UInt32)(left.register.uint32_2 * right.register.uint32_2);
+ product += (UInt32)(left.register.uint32_3 * right.register.uint32_3);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32 product = 0;
+ product += (Int32)(left.register.int32_0 * right.register.int32_0);
+ product += (Int32)(left.register.int32_1 * right.register.int32_1);
+ product += (Int32)(left.register.int32_2 * right.register.int32_2);
+ product += (Int32)(left.register.int32_3 * right.register.int32_3);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64 product = 0;
+ product += (UInt64)(left.register.uint64_0 * right.register.uint64_0);
+ product += (UInt64)(left.register.uint64_1 * right.register.uint64_1);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64 product = 0;
+ product += (Int64)(left.register.int64_0 * right.register.int64_0);
+ product += (Int64)(left.register.int64_1 * right.register.int64_1);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single product = 0;
+ product += (Single)(left.register.single_0 * right.register.single_0);
+ product += (Single)(left.register.single_1 * right.register.single_1);
+ product += (Single)(left.register.single_2 * right.register.single_2);
+ product += (Single)(left.register.single_3 * right.register.single_3);
+ return (T)(object)product;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double product = 0;
+ product += (Double)(left.register.double_0 * right.register.double_0);
+ product += (Double)(left.register.double_1 * right.register.double_1);
+ return (T)(object)product;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> SquareRoot(Vector<T> value)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte* dataPtr = stackalloc Byte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Byte)Math.Sqrt((Byte)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte* dataPtr = stackalloc SByte[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((SByte)Math.Sqrt((SByte)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16* dataPtr = stackalloc UInt16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((UInt16)Math.Sqrt((UInt16)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16* dataPtr = stackalloc Int16[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Int16)Math.Sqrt((Int16)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32* dataPtr = stackalloc UInt32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((UInt32)Math.Sqrt((UInt32)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32* dataPtr = stackalloc Int32[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Int32)Math.Sqrt((Int32)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64* dataPtr = stackalloc UInt64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((UInt64)Math.Sqrt((UInt64)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64* dataPtr = stackalloc Int64[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Int64)Math.Sqrt((Int64)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single* dataPtr = stackalloc Single[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Single)Math.Sqrt((Single)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double* dataPtr = stackalloc Double[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((Double)Math.Sqrt((Double)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ value.register.byte_0 = (Byte)Math.Sqrt(value.register.byte_0);
+ value.register.byte_1 = (Byte)Math.Sqrt(value.register.byte_1);
+ value.register.byte_2 = (Byte)Math.Sqrt(value.register.byte_2);
+ value.register.byte_3 = (Byte)Math.Sqrt(value.register.byte_3);
+ value.register.byte_4 = (Byte)Math.Sqrt(value.register.byte_4);
+ value.register.byte_5 = (Byte)Math.Sqrt(value.register.byte_5);
+ value.register.byte_6 = (Byte)Math.Sqrt(value.register.byte_6);
+ value.register.byte_7 = (Byte)Math.Sqrt(value.register.byte_7);
+ value.register.byte_8 = (Byte)Math.Sqrt(value.register.byte_8);
+ value.register.byte_9 = (Byte)Math.Sqrt(value.register.byte_9);
+ value.register.byte_10 = (Byte)Math.Sqrt(value.register.byte_10);
+ value.register.byte_11 = (Byte)Math.Sqrt(value.register.byte_11);
+ value.register.byte_12 = (Byte)Math.Sqrt(value.register.byte_12);
+ value.register.byte_13 = (Byte)Math.Sqrt(value.register.byte_13);
+ value.register.byte_14 = (Byte)Math.Sqrt(value.register.byte_14);
+ value.register.byte_15 = (Byte)Math.Sqrt(value.register.byte_15);
+ return value;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ value.register.sbyte_0 = (SByte)Math.Sqrt(value.register.sbyte_0);
+ value.register.sbyte_1 = (SByte)Math.Sqrt(value.register.sbyte_1);
+ value.register.sbyte_2 = (SByte)Math.Sqrt(value.register.sbyte_2);
+ value.register.sbyte_3 = (SByte)Math.Sqrt(value.register.sbyte_3);
+ value.register.sbyte_4 = (SByte)Math.Sqrt(value.register.sbyte_4);
+ value.register.sbyte_5 = (SByte)Math.Sqrt(value.register.sbyte_5);
+ value.register.sbyte_6 = (SByte)Math.Sqrt(value.register.sbyte_6);
+ value.register.sbyte_7 = (SByte)Math.Sqrt(value.register.sbyte_7);
+ value.register.sbyte_8 = (SByte)Math.Sqrt(value.register.sbyte_8);
+ value.register.sbyte_9 = (SByte)Math.Sqrt(value.register.sbyte_9);
+ value.register.sbyte_10 = (SByte)Math.Sqrt(value.register.sbyte_10);
+ value.register.sbyte_11 = (SByte)Math.Sqrt(value.register.sbyte_11);
+ value.register.sbyte_12 = (SByte)Math.Sqrt(value.register.sbyte_12);
+ value.register.sbyte_13 = (SByte)Math.Sqrt(value.register.sbyte_13);
+ value.register.sbyte_14 = (SByte)Math.Sqrt(value.register.sbyte_14);
+ value.register.sbyte_15 = (SByte)Math.Sqrt(value.register.sbyte_15);
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ value.register.uint16_0 = (UInt16)Math.Sqrt(value.register.uint16_0);
+ value.register.uint16_1 = (UInt16)Math.Sqrt(value.register.uint16_1);
+ value.register.uint16_2 = (UInt16)Math.Sqrt(value.register.uint16_2);
+ value.register.uint16_3 = (UInt16)Math.Sqrt(value.register.uint16_3);
+ value.register.uint16_4 = (UInt16)Math.Sqrt(value.register.uint16_4);
+ value.register.uint16_5 = (UInt16)Math.Sqrt(value.register.uint16_5);
+ value.register.uint16_6 = (UInt16)Math.Sqrt(value.register.uint16_6);
+ value.register.uint16_7 = (UInt16)Math.Sqrt(value.register.uint16_7);
+ return value;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ value.register.int16_0 = (Int16)Math.Sqrt(value.register.int16_0);
+ value.register.int16_1 = (Int16)Math.Sqrt(value.register.int16_1);
+ value.register.int16_2 = (Int16)Math.Sqrt(value.register.int16_2);
+ value.register.int16_3 = (Int16)Math.Sqrt(value.register.int16_3);
+ value.register.int16_4 = (Int16)Math.Sqrt(value.register.int16_4);
+ value.register.int16_5 = (Int16)Math.Sqrt(value.register.int16_5);
+ value.register.int16_6 = (Int16)Math.Sqrt(value.register.int16_6);
+ value.register.int16_7 = (Int16)Math.Sqrt(value.register.int16_7);
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ value.register.uint32_0 = (UInt32)Math.Sqrt(value.register.uint32_0);
+ value.register.uint32_1 = (UInt32)Math.Sqrt(value.register.uint32_1);
+ value.register.uint32_2 = (UInt32)Math.Sqrt(value.register.uint32_2);
+ value.register.uint32_3 = (UInt32)Math.Sqrt(value.register.uint32_3);
+ return value;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ value.register.int32_0 = (Int32)Math.Sqrt(value.register.int32_0);
+ value.register.int32_1 = (Int32)Math.Sqrt(value.register.int32_1);
+ value.register.int32_2 = (Int32)Math.Sqrt(value.register.int32_2);
+ value.register.int32_3 = (Int32)Math.Sqrt(value.register.int32_3);
+ return value;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ value.register.uint64_0 = (UInt64)Math.Sqrt(value.register.uint64_0);
+ value.register.uint64_1 = (UInt64)Math.Sqrt(value.register.uint64_1);
+ return value;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ value.register.int64_0 = (Int64)Math.Sqrt(value.register.int64_0);
+ value.register.int64_1 = (Int64)Math.Sqrt(value.register.int64_1);
+ return value;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ value.register.single_0 = (Single)Math.Sqrt(value.register.single_0);
+ value.register.single_1 = (Single)Math.Sqrt(value.register.single_1);
+ value.register.single_2 = (Single)Math.Sqrt(value.register.single_2);
+ value.register.single_3 = (Single)Math.Sqrt(value.register.single_3);
+ return value;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ value.register.double_0 = (Double)Math.Sqrt(value.register.double_0);
+ value.register.double_1 = (Double)Math.Sqrt(value.register.double_1);
+ return value;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+ #endregion Internal Math Methods
+
+ #region Helper Methods
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarEquals(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (Byte)(object)left == (Byte)(object)right;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (SByte)(object)left == (SByte)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (UInt16)(object)left == (UInt16)(object)right;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (Int16)(object)left == (Int16)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (UInt32)(object)left == (UInt32)(object)right;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (Int32)(object)left == (Int32)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (UInt64)(object)left == (UInt64)(object)right;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (Int64)(object)left == (Int64)(object)right;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (Single)(object)left == (Single)(object)right;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (Double)(object)left == (Double)(object)right;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarLessThan(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (Byte)(object)left < (Byte)(object)right;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (SByte)(object)left < (SByte)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (UInt16)(object)left < (UInt16)(object)right;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (Int16)(object)left < (Int16)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (UInt32)(object)left < (UInt32)(object)right;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (Int32)(object)left < (Int32)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (UInt64)(object)left < (UInt64)(object)right;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (Int64)(object)left < (Int64)(object)right;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (Single)(object)left < (Single)(object)right;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (Double)(object)left < (Double)(object)right;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarGreaterThan(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (Byte)(object)left > (Byte)(object)right;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (SByte)(object)left > (SByte)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (UInt16)(object)left > (UInt16)(object)right;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (Int16)(object)left > (Int16)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (UInt32)(object)left > (UInt32)(object)right;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (Int32)(object)left > (Int32)(object)right;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (UInt64)(object)left > (UInt64)(object)right;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (Int64)(object)left > (Int64)(object)right;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (Single)(object)left > (Single)(object)right;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (Double)(object)left > (Double)(object)right;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarAdd(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (T)(object)unchecked((Byte)((Byte)(object)left + (Byte)(object)right));
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (T)(object)unchecked((SByte)((SByte)(object)left + (SByte)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (T)(object)unchecked((UInt16)((UInt16)(object)left + (UInt16)(object)right));
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (T)(object)unchecked((Int16)((Int16)(object)left + (Int16)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (T)(object)unchecked((UInt32)((UInt32)(object)left + (UInt32)(object)right));
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (T)(object)unchecked((Int32)((Int32)(object)left + (Int32)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (T)(object)unchecked((UInt64)((UInt64)(object)left + (UInt64)(object)right));
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (T)(object)unchecked((Int64)((Int64)(object)left + (Int64)(object)right));
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (T)(object)unchecked((Single)((Single)(object)left + (Single)(object)right));
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (T)(object)unchecked((Double)((Double)(object)left + (Double)(object)right));
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarSubtract(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (T)(object)(Byte)((Byte)(object)left - (Byte)(object)right);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (T)(object)(SByte)((SByte)(object)left - (SByte)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (T)(object)(UInt16)((UInt16)(object)left - (UInt16)(object)right);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (T)(object)(Int16)((Int16)(object)left - (Int16)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (T)(object)(UInt32)((UInt32)(object)left - (UInt32)(object)right);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (T)(object)(Int32)((Int32)(object)left - (Int32)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (T)(object)(UInt64)((UInt64)(object)left - (UInt64)(object)right);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (T)(object)(Int64)((Int64)(object)left - (Int64)(object)right);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (T)(object)(Single)((Single)(object)left - (Single)(object)right);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (T)(object)(Double)((Double)(object)left - (Double)(object)right);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarMultiply(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (T)(object)unchecked((Byte)((Byte)(object)left * (Byte)(object)right));
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (T)(object)unchecked((SByte)((SByte)(object)left * (SByte)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (T)(object)unchecked((UInt16)((UInt16)(object)left * (UInt16)(object)right));
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (T)(object)unchecked((Int16)((Int16)(object)left * (Int16)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (T)(object)unchecked((UInt32)((UInt32)(object)left * (UInt32)(object)right));
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (T)(object)unchecked((Int32)((Int32)(object)left * (Int32)(object)right));
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (T)(object)unchecked((UInt64)((UInt64)(object)left * (UInt64)(object)right));
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (T)(object)unchecked((Int64)((Int64)(object)left * (Int64)(object)right));
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (T)(object)unchecked((Single)((Single)(object)left * (Single)(object)right));
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (T)(object)unchecked((Double)((Double)(object)left * (Double)(object)right));
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarDivide(T left, T right)
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (T)(object)(Byte)((Byte)(object)left / (Byte)(object)right);
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (T)(object)(SByte)((SByte)(object)left / (SByte)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (T)(object)(UInt16)((UInt16)(object)left / (UInt16)(object)right);
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (T)(object)(Int16)((Int16)(object)left / (Int16)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (T)(object)(UInt32)((UInt32)(object)left / (UInt32)(object)right);
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (T)(object)(Int32)((Int32)(object)left / (Int32)(object)right);
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (T)(object)(UInt64)((UInt64)(object)left / (UInt64)(object)right);
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (T)(object)(Int64)((Int64)(object)left / (Int64)(object)right);
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (T)(object)(Single)((Single)(object)left / (Single)(object)right);
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (T)(object)(Double)((Double)(object)left / (Double)(object)right);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T GetOneValue()
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ Byte value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ SByte value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ UInt16 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ Int16 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ UInt32 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ Int32 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ UInt64 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ Int64 value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ Single value = 1;
+ return (T)(object)value;
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ Double value = 1;
+ return (T)(object)value;
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T GetAllBitsSetValue()
+ {
+ if (typeof(T) == typeof(Byte))
+ {
+ return (T)(object)ConstantHelper.GetByteWithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(SByte))
+ {
+ return (T)(object)ConstantHelper.GetSByteWithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(UInt16))
+ {
+ return (T)(object)ConstantHelper.GetUInt16WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(Int16))
+ {
+ return (T)(object)ConstantHelper.GetInt16WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(UInt32))
+ {
+ return (T)(object)ConstantHelper.GetUInt32WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(Int32))
+ {
+ return (T)(object)ConstantHelper.GetInt32WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(UInt64))
+ {
+ return (T)(object)ConstantHelper.GetUInt64WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(Int64))
+ {
+ return (T)(object)ConstantHelper.GetInt64WithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(Single))
+ {
+ return (T)(object)ConstantHelper.GetSingleWithAllBitsSet();
+ }
+ else if (typeof(T) == typeof(Double))
+ {
+ return (T)(object)ConstantHelper.GetDoubleWithAllBitsSet();
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ #endregion
+ }
+
+ [Intrinsic]
+ public static partial class Vector
+ {
+ #region Widen/Narrow
+ /// <summary>
+ /// Widens a Vector{Byte} into two Vector{UInt16}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe void Widen(Vector<Byte> source, out Vector<UInt16> low, out Vector<UInt16> high)
+ {
+ int elements = Vector<Byte>.Count;
+ UInt16* lowPtr = stackalloc UInt16[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (UInt16)source[i];
+ }
+ UInt16* highPtr = stackalloc UInt16[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (UInt16)source[i + (elements / 2)];
+ }
+
+ low = new Vector<UInt16>(lowPtr);
+ high = new Vector<UInt16>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{UInt16} into two Vector{UInt32}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe void Widen(Vector<UInt16> source, out Vector<UInt32> low, out Vector<UInt32> high)
+ {
+ int elements = Vector<UInt16>.Count;
+ UInt32* lowPtr = stackalloc UInt32[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (UInt32)source[i];
+ }
+ UInt32* highPtr = stackalloc UInt32[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (UInt32)source[i + (elements / 2)];
+ }
+
+ low = new Vector<UInt32>(lowPtr);
+ high = new Vector<UInt32>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{UInt32} into two Vector{UInt64}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe void Widen(Vector<UInt32> source, out Vector<UInt64> low, out Vector<UInt64> high)
+ {
+ int elements = Vector<UInt32>.Count;
+ UInt64* lowPtr = stackalloc UInt64[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (UInt64)source[i];
+ }
+ UInt64* highPtr = stackalloc UInt64[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (UInt64)source[i + (elements / 2)];
+ }
+
+ low = new Vector<UInt64>(lowPtr);
+ high = new Vector<UInt64>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{SByte} into two Vector{Int16}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe void Widen(Vector<SByte> source, out Vector<Int16> low, out Vector<Int16> high)
+ {
+ int elements = Vector<SByte>.Count;
+ Int16* lowPtr = stackalloc Int16[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (Int16)source[i];
+ }
+ Int16* highPtr = stackalloc Int16[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (Int16)source[i + (elements / 2)];
+ }
+
+ low = new Vector<Int16>(lowPtr);
+ high = new Vector<Int16>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{Int16} into two Vector{Int32}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe void Widen(Vector<Int16> source, out Vector<Int32> low, out Vector<Int32> high)
+ {
+ int elements = Vector<Int16>.Count;
+ Int32* lowPtr = stackalloc Int32[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (Int32)source[i];
+ }
+ Int32* highPtr = stackalloc Int32[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (Int32)source[i + (elements / 2)];
+ }
+
+ low = new Vector<Int32>(lowPtr);
+ high = new Vector<Int32>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{Int32} into two Vector{Int64}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe void Widen(Vector<Int32> source, out Vector<Int64> low, out Vector<Int64> high)
+ {
+ int elements = Vector<Int32>.Count;
+ Int64* lowPtr = stackalloc Int64[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (Int64)source[i];
+ }
+ Int64* highPtr = stackalloc Int64[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (Int64)source[i + (elements / 2)];
+ }
+
+ low = new Vector<Int64>(lowPtr);
+ high = new Vector<Int64>(highPtr);
+ }
+
+ /// <summary>
+ /// Widens a Vector{Single} into two Vector{Double}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe void Widen(Vector<Single> source, out Vector<Double> low, out Vector<Double> high)
+ {
+ int elements = Vector<Single>.Count;
+ Double* lowPtr = stackalloc Double[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (Double)source[i];
+ }
+ Double* highPtr = stackalloc Double[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (Double)source[i + (elements / 2)];
+ }
+
+ low = new Vector<Double>(lowPtr);
+ high = new Vector<Double>(highPtr);
+ }
+
+ /// <summary>
+ /// Narrows two Vector{UInt16}'s into one Vector{Byte}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{Byte} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<Byte> Narrow(Vector<UInt16> low, Vector<UInt16> high)
+ {
+ unchecked
+ {
+ int elements = Vector<Byte>.Count;
+ Byte* retPtr = stackalloc Byte[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (Byte)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (Byte)high[i];
+ }
+
+ return new Vector<Byte>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{UInt32}'s into one Vector{UInt16}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{UInt16} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<UInt16> Narrow(Vector<UInt32> low, Vector<UInt32> high)
+ {
+ unchecked
+ {
+ int elements = Vector<UInt16>.Count;
+ UInt16* retPtr = stackalloc UInt16[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (UInt16)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (UInt16)high[i];
+ }
+
+ return new Vector<UInt16>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{UInt64}'s into one Vector{UInt32}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{UInt32} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<UInt32> Narrow(Vector<UInt64> low, Vector<UInt64> high)
+ {
+ unchecked
+ {
+ int elements = Vector<UInt32>.Count;
+ UInt32* retPtr = stackalloc UInt32[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (UInt32)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (UInt32)high[i];
+ }
+
+ return new Vector<UInt32>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{Int16}'s into one Vector{SByte}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{SByte} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<SByte> Narrow(Vector<Int16> low, Vector<Int16> high)
+ {
+ unchecked
+ {
+ int elements = Vector<SByte>.Count;
+ SByte* retPtr = stackalloc SByte[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (SByte)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (SByte)high[i];
+ }
+
+ return new Vector<SByte>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{Int32}'s into one Vector{Int16}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{Int16} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe Vector<Int16> Narrow(Vector<Int32> low, Vector<Int32> high)
+ {
+ unchecked
+ {
+ int elements = Vector<Int16>.Count;
+ Int16* retPtr = stackalloc Int16[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (Int16)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (Int16)high[i];
+ }
+
+ return new Vector<Int16>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{Int64}'s into one Vector{Int32}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{Int32} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe Vector<Int32> Narrow(Vector<Int64> low, Vector<Int64> high)
+ {
+ unchecked
+ {
+ int elements = Vector<Int32>.Count;
+ Int32* retPtr = stackalloc Int32[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (Int32)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (Int32)high[i];
+ }
+
+ return new Vector<Int32>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Narrows two Vector{Double}'s into one Vector{Single}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{Single} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+ [Intrinsic]
+ public static unsafe Vector<Single> Narrow(Vector<Double> low, Vector<Double> high)
+ {
+ unchecked
+ {
+ int elements = Vector<Single>.Count;
+ Single* retPtr = stackalloc Single[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (Single)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (Single)high[i];
+ }
+
+ return new Vector<Single>(retPtr);
+ }
+ }
+
+ #endregion Widen/Narrow
+
+ #region Same-Size Conversion
+ /// <summary>
+ /// Converts a Vector{Int32} to a Vector{Single}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<Single> ConvertToSingle(Vector<Int32> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Single>.Count;
+ Single* retPtr = stackalloc Single[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Single)value[i];
+ }
+
+ return new Vector<Single>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{UInt32} to a Vector{Single}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<Single> ConvertToSingle(Vector<UInt32> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Single>.Count;
+ Single* retPtr = stackalloc Single[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Single)value[i];
+ }
+
+ return new Vector<Single>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{Int64} to a Vector{Double}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<Double> ConvertToDouble(Vector<Int64> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Double>.Count;
+ Double* retPtr = stackalloc Double[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Double)value[i];
+ }
+
+ return new Vector<Double>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{UInt64} to a Vector{Double}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<Double> ConvertToDouble(Vector<UInt64> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Double>.Count;
+ Double* retPtr = stackalloc Double[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Double)value[i];
+ }
+
+ return new Vector<Double>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{Single} to a Vector{Int32}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<Int32> ConvertToInt32(Vector<Single> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Int32>.Count;
+ Int32* retPtr = stackalloc Int32[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Int32)value[i];
+ }
+
+ return new Vector<Int32>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{Single} to a Vector{UInt32}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<UInt32> ConvertToUInt32(Vector<Single> value)
+ {
+ unchecked
+ {
+ int elements = Vector<UInt32>.Count;
+ UInt32* retPtr = stackalloc UInt32[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (UInt32)value[i];
+ }
+
+ return new Vector<UInt32>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{Double} to a Vector{Int64}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<Int64> ConvertToInt64(Vector<Double> value)
+ {
+ unchecked
+ {
+ int elements = Vector<Int64>.Count;
+ Int64* retPtr = stackalloc Int64[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (Int64)value[i];
+ }
+
+ return new Vector<Int64>(retPtr);
+ }
+ }
+
+ /// <summary>
+ /// Converts a Vector{Double} to a Vector{UInt64}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+ [CLSCompliant(false)]
+ [Intrinsic]
+ public static unsafe Vector<UInt64> ConvertToUInt64(Vector<Double> value)
+ {
+ unchecked
+ {
+ int elements = Vector<UInt64>.Count;
+ UInt64* retPtr = stackalloc UInt64[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (UInt64)value[i];
+ }
+
+ return new Vector<UInt64>(retPtr);
+ }
+ }
+
+ #endregion Same-Size Conversion
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/Vector.tt b/src/System.Private.CoreLib/shared/System/Numerics/Vector.tt
new file mode 100644
index 000000000..275f47350
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/Vector.tt
@@ -0,0 +1,1808 @@
+<#@ template debug="true" hostSpecific="true" #>
+<#@ output extension=".cs" #>
+<#@ Assembly Name="System.Core.dll" #>
+<#@ Assembly Name="System.Xml.dll" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Runtime.InteropServices" #>
+<#@ include file="GenerationConfig.ttinclude" #><# GenerateCopyrightHeader(); #>
+
+using System.Globalization;
+using System.Numerics.Hashing;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace System.Numerics
+{
+ /* Note: The following patterns are used throughout the code here and are described here
+ *
+ * PATTERN:
+ * if (typeof(T) == typeof(Int32)) { ... }
+ * else if (typeof(T) == typeof(Single)) { ... }
+ * EXPLANATION:
+ * At runtime, each instantiation of Vector<T> will be type-specific, and each of these typeof blocks will be eliminated,
+ * as typeof(T) is a (JIT) compile-time constant for each instantiation. This design was chosen to eliminate any overhead from
+ * delegates and other patterns.
+ *
+ * PATTERN:
+ * if (Vector.IsHardwareAccelerated) { ... }
+ * else { ... }
+ * EXPLANATION
+ * This pattern solves two problems:
+ * 1. Allows us to unroll loops when we know the size (when no hardware acceleration is present)
+ * 2. Allows reflection to work:
+ * - If a method is called via reflection, it will not be "intrinsified", which would cause issues if we did
+ * not provide an implementation for that case (i.e. if it only included a case which assumed 16-byte registers)
+ * (NOTE: It is assumed that Vector.IsHardwareAccelerated will be a compile-time constant, eliminating these checks
+ * from the JIT'd code.)
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /// <summary>
+ /// A structure that represents a single Vector. The count of this Vector is fixed but CPU register dependent.
+ /// This struct only supports numerical types. This type is intended to be used as a building block for vectorizing
+ /// large algorithms. This type is immutable, individual elements cannot be modified.
+ /// </summary>
+ [Intrinsic]
+ public struct Vector<T> : IEquatable<Vector<T>>, IFormattable where T : struct
+ {
+ #region Fields
+ private Register register;
+ #endregion Fields
+
+ #region Static Members
+ /// <summary>
+ /// Returns the number of elements stored in the vector. This value is hardware dependent.
+ /// </summary>
+ public static int Count
+ {
+ [Intrinsic]
+ get
+ {
+ return s_count;
+ }
+ }
+ private static readonly int s_count = InitializeCount();
+
+ /// <summary>
+ /// Returns a vector containing all zeroes.
+ /// </summary>
+ public static Vector<T> Zero
+ {
+ [Intrinsic]
+ get
+ {
+ return s_zero;
+ }
+ }
+ private static readonly Vector<T> s_zero = new Vector<T>();
+
+ /// <summary>
+ /// Returns a vector containing all ones.
+ /// </summary>
+ public static Vector<T> One
+ {
+ [Intrinsic]
+ get
+ {
+ return s_one;
+ }
+ }
+ private static readonly Vector<T> s_one = new Vector<T>(GetOneValue());
+
+ internal static Vector<T> AllOnes { get { return s_allOnes; } }
+ private static readonly Vector<T> s_allOnes = new Vector<T>(GetAllBitsSetValue());
+ #endregion Static Members
+
+ #region Static Initialization
+ private struct VectorSizeHelper
+ {
+ internal Vector<T> _placeholder;
+ internal byte _byte;
+ }
+
+ // Calculates the size of this struct in bytes, by computing the offset of a field in a structure
+ private static unsafe int InitializeCount()
+ {
+ VectorSizeHelper vsh;
+ byte* vectorBase = &vsh._placeholder.register.byte_0;
+ byte* byteBase = &vsh._byte;
+ int vectorSizeInBytes = (int)(byteBase - vectorBase);
+
+ int typeSizeInBytes = -1;
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ typeSizeInBytes = sizeof(<#=type.Name#>);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+
+ return vectorSizeInBytes / typeSizeInBytes;
+ }
+ #endregion Static Initialization
+
+ #region Constructors
+ /// <summary>
+ /// Constructs a vector whose components are all <code>value</code>
+ /// </summary>
+ [Intrinsic]
+ public unsafe Vector(T value)
+ : this()
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ fixed (<#=type.Name#>* basePtr = &this.<#=GetRegisterFieldName(type, 0)#>)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (<#=type.Name#>)(object)value;
+ }
+ }
+ }
+<#
+ }
+#>
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < totalSize / Marshal.SizeOf(type); g++)
+ {
+#>
+ <#=GetRegisterFieldName(type, g)#> = (<#=type.Name#>)(object)value;
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ }
+ }
+
+ /// <summary>
+ /// Constructs a vector from the given array. The size of the given array must be at least Vector'T.Count.
+ /// </summary>
+ [Intrinsic]
+ public unsafe Vector(T[] values) : this(values, 0) { }
+
+ /// <summary>
+ /// Constructs a vector from the given array, starting from the given index.
+ /// The array must contain at least Vector'T.Count from the given index.
+ /// </summary>
+ public unsafe Vector(T[] values, int index)
+ : this()
+ {
+ if (values == null)
+ {
+ // Match the JIT's exception type here. For perf, a NullReference is thrown instead of an ArgumentNull.
+ throw new NullReferenceException(SR.Arg_NullArgumentNullRef);
+ }
+ if (index < 0 || (values.Length - index) < Count)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ fixed (<#=type.Name#>* basePtr = &this.<#=GetRegisterFieldName(type, 0)#>)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ *(basePtr + g) = (<#=type.Name#>)(object)values[g + index];
+ }
+ }
+ }
+<#
+ }
+#>
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ fixed (<#=type.Name#>* basePtr = &this.<#=GetRegisterFieldName(type, 0)#>)
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ *(basePtr + <#=g#>) = (<#=type.Name#>)(object)values[<#=g#> + index];
+<#
+ }
+#>
+ }
+ }
+<#
+ }
+#>
+ }
+ }
+
+#pragma warning disable 3001 // void* is not a CLS-Compliant argument type
+ internal unsafe Vector(void* dataPointer) : this(dataPointer, 0) { }
+#pragma warning restore 3001 // void* is not a CLS-Compliant argument type
+
+#pragma warning disable 3001 // void* is not a CLS-Compliant argument type
+ // Implemented with offset if this API ever becomes public; an offset of 0 is used internally.
+ internal unsafe Vector(void* dataPointer, int offset)
+ : this()
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* castedPtr = (<#=type.Name#>*)dataPointer;
+ castedPtr += offset;
+ fixed (<#=type.Name#>* registerBase = &this.<#=GetRegisterFieldName(type, 0)#>)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ registerBase[g] = castedPtr[g];
+ }
+ }
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+#pragma warning restore 3001 // void* is not a CLS-Compliant argument type
+
+ private Vector(ref Register existingRegister)
+ {
+ this.register = existingRegister;
+ }
+ #endregion Constructors
+
+ #region Public Instance Methods
+ /// <summary>
+ /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
+ /// </summary>
+ /// <param name="destination">The destination array which the values are copied into</param>
+ /// <exception cref="ArgumentNullException">If the destination array is null</exception>
+ /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination array</exception>
+ [Intrinsic]
+ public unsafe void CopyTo(T[] destination)
+ {
+ CopyTo(destination, 0);
+ }
+
+ /// <summary>
+ /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
+ /// </summary>
+ /// <param name="destination">The destination array which the values are copied into</param>
+ /// <param name="startIndex">The index to start copying to</param>
+ /// <exception cref="ArgumentNullException">If the destination array is null</exception>
+ /// <exception cref="ArgumentOutOfRangeException">If index is greater than end of the array or index is less than zero</exception>
+ /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination array</exception>
+ [Intrinsic]
+ public unsafe void CopyTo(T[] destination, int startIndex)
+ {
+ if (destination == null)
+ {
+ // Match the JIT's exception type here. For perf, a NullReference is thrown instead of an ArgumentNull.
+ throw new NullReferenceException(SR.Arg_NullArgumentNullRef);
+ }
+ if (startIndex < 0 || startIndex >= destination.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Format(SR.Arg_ArgumentOutOfRangeException, startIndex));
+ }
+ if ((destination.Length - startIndex) < Count)
+ {
+ throw new ArgumentException(SR.Format(SR.Arg_ElementsInSourceIsGreaterThanDestination, startIndex));
+ }
+
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>[] <#=type.Name.ToLowerInvariant()#>Array = (<#=type.Name#>[])(object)destination;
+ fixed (<#=type.Name#>* destinationBase = <#=type.Name.ToLowerInvariant()#>Array)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ destinationBase[startIndex + g] = (<#=type.Name#>)(object)this[g];
+ }
+ }
+ }
+<#
+ }
+#>
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>[] <#=type.Name.ToLowerInvariant()#>Array = (<#=type.Name#>[])(object)destination;
+ fixed (<#=type.Name#>* destinationBase = <#=type.Name.ToLowerInvariant()#>Array)
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ destinationBase[startIndex + <#=g#>] = this.<#=GetRegisterFieldName(type, g)#>;
+<#
+ }
+#>
+ }
+ }
+<#
+ }
+#>
+ }
+ }
+
+ /// <summary>
+ /// Returns the element at the given index.
+ /// </summary>
+ public unsafe T this[int index]
+ {
+ [Intrinsic]
+ get
+ {
+ if (index >= Count || index < 0)
+ {
+ throw new IndexOutOfRangeException(SR.Format(SR.Arg_ArgumentOutOfRangeException, index));
+ }
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ fixed (<#=type.Name#>* basePtr = &this.<#=GetRegisterFieldName(type, 0)#>)
+ {
+ return (T)(object)*(basePtr + index);
+ }
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether the given Object is equal to this vector instance.
+ /// </summary>
+ /// <param name="obj">The Object to compare against.</param>
+ /// <returns>True if the Object is equal to this vector; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object obj)
+ {
+ if (!(obj is Vector<T>))
+ {
+ return false;
+ }
+ return Equals((Vector<T>)obj);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether the given vector is equal to this vector instance.
+ /// </summary>
+ /// <param name="other">The vector to compare this instance to.</param>
+ /// <returns>True if the other vector is equal to this instance; False otherwise.</returns>
+ [Intrinsic]
+ public bool Equals(Vector<T> other)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ if (!ScalarEquals(this[g], other[g]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+<#
+ if (g == 0)
+ {
+#>
+ this.<#=GetRegisterFieldName(type, g)#> == other.<#=GetRegisterFieldName(type, g)#>
+<#
+ }
+ else
+ {
+#>
+ && this.<#=GetRegisterFieldName(type, g)#> == other.<#=GetRegisterFieldName(type, g)#><#=(g == (GetNumFields(type, totalSize) -1)) ? ";" : ""#>
+<#
+ }
+#>
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the hash code for this instance.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ public override int GetHashCode()
+ {
+ int hash = 0;
+
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ for (int g = 0; g < Count; g++)
+ {
+ hash = HashHelpers.Combine(hash, ((<#=type.Name#>)(object)this[g]).GetHashCode());
+ }
+ return hash;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ hash = HashHelpers.Combine(hash, this.<#=GetRegisterFieldName(type, g)#>.GetHashCode());
+<#
+ }
+#>
+ return hash;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector.
+ /// </summary>
+ /// <returns>The string representation.</returns>
+ public override string ToString()
+ {
+ return ToString("G", CultureInfo.CurrentCulture);
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector, using the specified format string to format individual elements.
+ /// </summary>
+ /// <param name="format">The format of individual elements.</param>
+ /// <returns>The string representation.</returns>
+ public string ToString(string format)
+ {
+ return ToString(format, CultureInfo.CurrentCulture);
+ }
+
+ /// <summary>
+ /// Returns a String representing this vector, using the specified format string to format individual elements
+ /// and the given IFormatProvider.
+ /// </summary>
+ /// <param name="format">The format of individual elements.</param>
+ /// <param name="formatProvider">The format provider to use when formatting elements.</param>
+ /// <returns>The string representation.</returns>
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ StringBuilder sb = new StringBuilder();
+ string separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator;
+ sb.Append('<');
+ for (int g = 0; g < Count - 1; g++)
+ {
+ sb.Append(((IFormattable)this[g]).ToString(format, formatProvider));
+ sb.Append(separator);
+ sb.Append(' ');
+ }
+ // Append last element w/out separator
+ sb.Append(((IFormattable)this[Count - 1]).ToString(format, formatProvider));
+ sb.Append('>');
+ return sb.ToString();
+ }
+ #endregion Public Instance Methods
+
+ #region Arithmetic Operators
+ /// <summary>
+ /// Adds two vectors together.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The summed vector.</returns>
+ public static unsafe Vector<T> operator +(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (<#=type.Name#>)(object)ScalarAdd(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> sum = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ sum.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(left.<#= GetRegisterFieldName(type, g) #> + right.<#= GetRegisterFieldName(type, g) #>);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return sum;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Subtracts the second vector from the first.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The difference vector.</returns>
+ public static unsafe Vector<T> operator -(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (<#=type.Name#>)(object)ScalarSubtract(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> difference = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ difference.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(left.<#= GetRegisterFieldName(type, g) #> - right.<#= GetRegisterFieldName(type, g) #>);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return difference;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies two vectors together.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The product vector.</returns>
+ public static unsafe Vector<T> operator *(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (<#=type.Name#>)(object)ScalarMultiply(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ product.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(left.<#= GetRegisterFieldName(type, g) #> * right.<#= GetRegisterFieldName(type, g) #>);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies a vector by the given scalar.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <param name="factor">The scalar value.</param>
+ /// <returns>The scaled vector.</returns>
+ public static Vector<T> operator *(Vector<T> value, T factor)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ return new Vector<T>(factor) * value;
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ product.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(value.<#= GetRegisterFieldName(type, g) #> * (<#=type.Name#>)(object)factor);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Multiplies a vector by the given scalar.
+ /// </summary>
+ /// <param name="factor">The scalar value.</param>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The scaled vector.</returns>
+ public static Vector<T> operator *(T factor, Vector<T> value)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ return new Vector<T>(factor) * value;
+ }
+ else
+ {
+ Vector<T> product = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ product.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(value.<#= GetRegisterFieldName(type, g) #> * (<#=type.Name#>)(object)factor);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return product;
+ }
+ }
+ }
+
+ // This method is intrinsic only for certain types. It cannot access fields directly unless we are sure the context is unaccelerated.
+ /// <summary>
+ /// Divides the first vector by the second.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The vector resulting from the division.</returns>
+ public static unsafe Vector<T> operator /(Vector<T> left, Vector<T> right)
+ {
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (<#=type.Name#>)(object)ScalarDivide(left[g], right[g]);
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> quotient = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ quotient.<#= GetRegisterFieldName(type, g) #> = (<#=type.Name#>)(left.<#= GetRegisterFieldName(type, g) #> / right.<#= GetRegisterFieldName(type, g) #>);
+<#
+ }
+#>
+ }
+<#
+ }
+#>
+ return quotient;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Negates a given vector.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The negated vector.</returns>
+ public static Vector<T> operator -(Vector<T> value)
+ {
+ return Zero - value;
+ }
+ #endregion Arithmetic Operators
+
+ #region Bitwise Operators
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-and operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator &(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] & rightBase[g];
+ }
+ }
+ else
+ {
+ result.<#=GetRegisterFieldName(typeof(Int64), 0)#> = left.<#=GetRegisterFieldName(typeof(Int64), 0)#> & right.<#=GetRegisterFieldName(typeof(Int64), 0)#>;
+ result.<#=GetRegisterFieldName(typeof(Int64), 1)#> = left.<#=GetRegisterFieldName(typeof(Int64), 1)#> & right.<#=GetRegisterFieldName(typeof(Int64), 1)#>;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator |(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] | rightBase[g];
+ }
+ }
+ else
+ {
+ result.<#=GetRegisterFieldName(typeof(Int64), 0)#> = left.<#=GetRegisterFieldName(typeof(Int64), 0)#> | right.<#=GetRegisterFieldName(typeof(Int64), 0)#>;
+ result.<#=GetRegisterFieldName(typeof(Int64), 1)#> = left.<#=GetRegisterFieldName(typeof(Int64), 1)#> | right.<#=GetRegisterFieldName(typeof(Int64), 1)#>;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-exclusive-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [Intrinsic]
+ public static unsafe Vector<T> operator ^(Vector<T> left, Vector<T> right)
+ {
+ Vector<T> result = new Vector<T>();
+ unchecked
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ Int64* resultBase = &result.register.int64_0;
+ Int64* leftBase = &left.register.int64_0;
+ Int64* rightBase = &right.register.int64_0;
+ for (int g = 0; g < Vector<Int64>.Count; g++)
+ {
+ resultBase[g] = leftBase[g] ^ rightBase[g];
+ }
+ }
+ else
+ {
+ result.<#=GetRegisterFieldName(typeof(Int64), 0)#> = left.<#=GetRegisterFieldName(typeof(Int64), 0)#> ^ right.<#=GetRegisterFieldName(typeof(Int64), 0)#>;
+ result.<#=GetRegisterFieldName(typeof(Int64), 1)#> = left.<#=GetRegisterFieldName(typeof(Int64), 1)#> ^ right.<#=GetRegisterFieldName(typeof(Int64), 1)#>;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are obtained by taking the one's complement of the given vector's elements.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The one's complement vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> operator ~(Vector<T> value)
+ {
+ return s_allOnes ^ value;
+ }
+ #endregion Bitwise Operators
+
+ #region Logical Operators
+ /// <summary>
+ /// Returns a boolean indicating whether each pair of elements in the given vectors are equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The first vector to compare.</param>
+ /// <returns>True if all elements are equal; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Vector<T> left, Vector<T> right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any single pair of elements in the given vectors are not equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if left and right are not equal; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Vector<T> left, Vector<T> right)
+ {
+ return !(left == right);
+ }
+ #endregion Logical Operators
+
+ #region Conversions
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of another type.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+<#
+ if (nonClsCompliantTypes.Contains(type))
+ {
+#>
+ [CLSCompliant(false)]
+<#
+ }
+#>
+ [Intrinsic]
+ public static explicit operator Vector<<#=type.Name#>>(Vector<T> value)
+ {
+ return new Vector<<#=type.Name#>>(ref value.register);
+ }
+
+<#
+ }
+#>
+ #endregion Conversions
+
+ #region Internal Comparison Methods
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> Equals(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarEquals(left[g], right[g]) ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ <#= GetRegisterFieldName(type, g) #> = left.<#= GetRegisterFieldName(type, g) #> == right.<#= GetRegisterFieldName(type, g) #> ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+<#
+ }
+#>
+ return new Vector<T>(ref register);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> LessThan(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ <#= GetRegisterFieldName(type, g) #> = left.<#= GetRegisterFieldName(type, g) #> < right.<#= GetRegisterFieldName(type, g) #> ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+<#
+ }
+#>
+ return new Vector<T>(ref register);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Vector<T> GreaterThan(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Register register = new Register();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ <#= GetRegisterFieldName(type, g) #> = left.<#= GetRegisterFieldName(type, g) #> > right.<#= GetRegisterFieldName(type, g) #> ? ConstantHelper.Get<#=type.Name#>WithAllBitsSet() : (<#=type.Name#>)0;
+<#
+ }
+#>
+ return new Vector<T>(ref register);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static Vector<T> GreaterThanOrEqual(Vector<T> left, Vector<T> right)
+ {
+ return Equals(left, right) | GreaterThan(left, right);
+ }
+
+ [Intrinsic]
+ internal static Vector<T> LessThanOrEqual(Vector<T> left, Vector<T> right)
+ {
+ return Equals(left, right) | LessThan(left, right);
+ }
+
+ [Intrinsic]
+ internal static Vector<T> ConditionalSelect(Vector<T> condition, Vector<T> left, Vector<T> right)
+ {
+ return (left & condition) | (Vector.AndNot(right, condition));
+ }
+ #endregion Comparison Methods
+
+ #region Internal Math Methods
+ [Intrinsic]
+ internal static unsafe Vector<T> Abs(Vector<T> value)
+ {
+<#
+ foreach (Type type in supportedTypes)
+ {
+ if (unsignedTypes.Contains(type))
+ {
+#>
+ <#=GenerateIfStatementHeader(type, unsignedTypes)#>
+ {
+ return value;
+ }
+<#
+ }
+ }
+#>
+ if (Vector.IsHardwareAccelerated)
+ {
+<#
+ foreach (Type type in supportedTypes.Except(unsignedTypes))
+ {
+#>
+ <#=GenerateIfStatementHeader(type, supportedTypes.Except(unsignedTypes))#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = (<#=type.Name#>)(object)(Math.Abs((<#=type.Name#>)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+<#
+ foreach (Type type in supportedTypes.Except(unsignedTypes))
+ {
+#>
+ <#=GenerateIfStatementHeader(type, supportedTypes.Except(unsignedTypes))#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ value.<#=GetRegisterFieldName(type, g)#> = (<#=type.Name#>)(Math.Abs(value.<#=GetRegisterFieldName(type, g)#>));
+<#
+ }
+#>
+ return value;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> Min(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarLessThan(left[g], right[g]) ? (<#=type.Name#>)(object)left[g] : (<#=type.Name#>)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> vec = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ vec.<#=GetRegisterFieldName(type, g)#> = left.<#=GetRegisterFieldName(type, g)#> < right.<#=GetRegisterFieldName(type, g)#> ? left.<#=GetRegisterFieldName(type, g)#> : right.<#=GetRegisterFieldName(type, g)#>;
+<#
+ }
+#>
+ return vec;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> Max(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = ScalarGreaterThan(left[g], right[g]) ? (<#=type.Name#>)(object)left[g] : (<#=type.Name#>)(object)right[g];
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+ Vector<T> vec = new Vector<T>();
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ vec.<#=GetRegisterFieldName(type, g)#> = left.<#=GetRegisterFieldName(type, g)#> > right.<#=GetRegisterFieldName(type, g)#> ? left.<#=GetRegisterFieldName(type, g)#> : right.<#=GetRegisterFieldName(type, g)#>;
+<#
+ }
+#>
+ return vec;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static T DotProduct(Vector<T> left, Vector<T> right)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+ T product = default;
+ for (int g = 0; g < Count; g++)
+ {
+ product = ScalarAdd(product, ScalarMultiply(left[g], right[g]));
+ }
+ return product;
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+ #>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#> product = 0;
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ product += (<#=type.Name#>)(left.<#=GetRegisterFieldName(type, g)#> * right.<#=GetRegisterFieldName(type, g)#>);
+<#
+ }
+#>
+ return (T)(object)product;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+
+ [Intrinsic]
+ internal static unsafe Vector<T> SquareRoot(Vector<T> value)
+ {
+ if (Vector.IsHardwareAccelerated)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#>* dataPtr = stackalloc <#=type.Name#>[Count];
+ for (int g = 0; g < Count; g++)
+ {
+ dataPtr[g] = unchecked((<#=type.Name#>)Math.Sqrt((<#=type.Name#>)(object)value[g]));
+ }
+ return new Vector<T>(dataPtr);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ else
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+<#
+ for (int g = 0; g < GetNumFields(type, totalSize); g++)
+ {
+#>
+ value.<#=GetRegisterFieldName(type, g)#> = (<#=type.Name#>)Math.Sqrt(value.<#=GetRegisterFieldName(type, g)#>);
+<#
+ }
+#>
+ return value;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ }
+ #endregion Internal Math Methods
+
+ #region Helper Methods
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarEquals(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (<#=type.Name#>)(object)left == (<#=type.Name#>)(object)right;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarLessThan(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (<#=type.Name#>)(object)left < (<#=type.Name#>)(object)right;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static bool ScalarGreaterThan(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (<#=type.Name#>)(object)left > (<#=type.Name#>)(object)right;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarAdd(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (T)(object)unchecked((<#=type.Name#>)((<#=type.Name#>)(object)left + (<#=type.Name#>)(object)right));
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarSubtract(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (T)(object)(<#=type.Name#>)((<#=type.Name#>)(object)left - (<#=type.Name#>)(object)right);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarMultiply(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (T)(object)unchecked((<#=type.Name#>)((<#=type.Name#>)(object)left * (<#=type.Name#>)(object)right));
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T ScalarDivide(T left, T right)
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (T)(object)(<#=type.Name#>)((<#=type.Name#>)(object)left / (<#=type.Name#>)(object)right);
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T GetOneValue()
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ <#=type.Name#> value = 1;
+ return (T)(object)value;
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ private static T GetAllBitsSetValue()
+ {
+<# foreach (Type type in supportedTypes)
+ {
+#>
+ <#=GenerateIfStatementHeader(type)#>
+ {
+ return (T)(object)ConstantHelper.Get<#=type.Name#>WithAllBitsSet();
+ }
+<#
+ }
+#>
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+ #endregion
+ }
+
+ [Intrinsic]
+ public static partial class Vector
+ {
+ #region Widen/Narrow
+<# foreach (Type type in WidenableTypes)
+ {
+ Type widenTarget = GetWidenTarget(type);
+#>
+ /// <summary>
+ /// Widens a Vector{<#=type.Name#>} into two Vector{<#=widenTarget.Name#>}'s.
+ /// <param name="source">The source vector whose elements are widened into the outputs.</param>
+ /// <param name="low">The first output vector, whose elements will contain the widened elements from lower indices in the source vector.</param>
+ /// <param name="high">The second output vector, whose elements will contain the widened elements from higher indices in the source vector.</param>
+ /// </summary>
+<#
+ if (nonClsCompliantTypes.Contains(type) || nonClsCompliantTypes.Contains(widenTarget))
+ {
+#>
+ [CLSCompliant(false)]
+<#
+ }
+#>
+ [Intrinsic]
+ public static unsafe void Widen(Vector<<#=type.Name#>> source, out Vector<<#=widenTarget.Name#>> low, out Vector<<#=widenTarget.Name#>> high)
+ {
+ int elements = Vector<<#=type.Name#>>.Count;
+ <#=widenTarget.Name#>* lowPtr = stackalloc <#=widenTarget.Name#>[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ lowPtr[i] = (<#=widenTarget.Name#>)source[i];
+ }
+ <#=widenTarget.Name#>* highPtr = stackalloc <#=widenTarget.Name#>[elements / 2];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ highPtr[i] = (<#=widenTarget.Name#>)source[i + (elements / 2)];
+ }
+
+ low = new Vector<<#=widenTarget.Name#>>(lowPtr);
+ high = new Vector<<#=widenTarget.Name#>>(highPtr);
+ }
+
+<#
+ }
+#>
+<# foreach (Type narrowSource in NarrowableTypes)
+ {
+ Type narrowTarget = GetNarrowTarget(narrowSource);
+#>
+ /// <summary>
+ /// Narrows two Vector{<#=narrowSource.Name#>}'s into one Vector{<#=narrowTarget.Name#>}.
+ /// <param name="low">The first source vector, whose elements become the lower-index elements of the return value.</param>
+ /// <param name="high">The second source vector, whose elements become the higher-index elements of the return value.</param>
+ /// <returns>A Vector{<#=narrowTarget.Name#>} containing elements narrowed from the source vectors.</returns>
+ /// </summary>
+<#
+ if (nonClsCompliantTypes.Contains(narrowSource) || nonClsCompliantTypes.Contains(narrowTarget))
+ {
+#>
+ [CLSCompliant(false)]
+<#
+ }
+#>
+ [Intrinsic]
+ public static unsafe Vector<<#=narrowTarget.Name#>> Narrow(Vector<<#=narrowSource.Name#>> low, Vector<<#=narrowSource.Name#>> high)
+ {
+ unchecked
+ {
+ int elements = Vector<<#=narrowTarget.Name#>>.Count;
+ <#=narrowTarget.Name#>* retPtr = stackalloc <#=narrowTarget.Name#>[elements];
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i] = (<#=narrowTarget.Name#>)low[i];
+ }
+ for (int i = 0; i < elements / 2; i++)
+ {
+ retPtr[i + (elements / 2)] = (<#=narrowTarget.Name#>)high[i];
+ }
+
+ return new Vector<<#=narrowTarget.Name#>>(retPtr);
+ }
+ }
+
+<#
+ }
+#>
+ #endregion Widen/Narrow
+
+ #region Same-Size Conversion
+<# foreach (var pair in SameSizeConversionPairs)
+ {
+#>
+ /// <summary>
+ /// Converts a Vector{<#=pair.Key.Name#>} to a Vector{<#=pair.Value.Name#>}.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The converted vector.</returns>
+<#
+ if (nonClsCompliantTypes.Contains(pair.Key) || nonClsCompliantTypes.Contains(pair.Value))
+ {
+#>
+ [CLSCompliant(false)]
+<#
+ }
+#>
+ [Intrinsic]
+ public static unsafe Vector<<#=pair.Value.Name#>> ConvertTo<#=pair.Value.Name#>(Vector<<#=pair.Key.Name#>> value)
+ {
+ unchecked
+ {
+ int elements = Vector<<#=pair.Value.Name#>>.Count;
+ <#=pair.Value.Name#>* retPtr = stackalloc <#=pair.Value.Name#>[elements];
+ for (int i = 0; i < elements; i++)
+ {
+ retPtr[i] = (<#=pair.Value.Name#>)value[i];
+ }
+
+ return new Vector<<#=pair.Value.Name#>>(retPtr);
+ }
+ }
+
+<# } #>
+ #endregion Same-Size Conversion
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Numerics/Vector_Operations.cs b/src/System.Private.CoreLib/shared/System/Numerics/Vector_Operations.cs
new file mode 100644
index 000000000..b69b058be
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Numerics/Vector_Operations.cs
@@ -0,0 +1,865 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+namespace System.Numerics
+{
+ /// <summary>
+ /// Contains various methods useful for creating, manipulating, combining, and converting generic vectors with one another.
+ /// </summary>
+ public static partial class Vector
+ {
+ // JIT is not looking at the Vector class methods
+ // all methods here should be inlined and they must be implemented in terms of Vector<T> intrinsics
+ #region Select Methods
+ /// <summary>
+ /// Creates a new vector with elements selected between the two given source vectors, and based on a mask vector.
+ /// </summary>
+ /// <param name="condition">The integral mask vector used to drive selection.</param>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The new vector with elements selected based on the mask.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Single> ConditionalSelect(Vector<int> condition, Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<Single>)Vector<Single>.ConditionalSelect((Vector<Single>)condition, left, right);
+ }
+
+ /// <summary>
+ /// Creates a new vector with elements selected between the two given source vectors, and based on a mask vector.
+ /// </summary>
+ /// <param name="condition">The integral mask vector used to drive selection.</param>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The new vector with elements selected based on the mask.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<double> ConditionalSelect(Vector<long> condition, Vector<double> left, Vector<double> right)
+ {
+ return (Vector<double>)Vector<double>.ConditionalSelect((Vector<double>)condition, left, right);
+ }
+
+ /// <summary>
+ /// Creates a new vector with elements selected between the two given source vectors, and based on a mask vector.
+ /// </summary>
+ /// <param name="condition">The mask vector used to drive selection.</param>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The new vector with elements selected based on the mask.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> ConditionalSelect<T>(Vector<T> condition, Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.ConditionalSelect(condition, left, right);
+ }
+ #endregion Select Methods
+
+ #region Comparison methods
+ #region Equals methods
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left and right were equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Equals<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.Equals(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether elements in the left and right floating point vectors were equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> Equals(Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<int>)Vector<Single>.Equals(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left and right were equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> Equals(Vector<int> left, Vector<int> right)
+ {
+ return Vector<int>.Equals(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether elements in the left and right floating point vectors were equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> Equals(Vector<double> left, Vector<double> right)
+ {
+ return (Vector<long>)Vector<double>.Equals(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left and right were equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> Equals(Vector<long> left, Vector<long> right)
+ {
+ return Vector<long>.Equals(left, right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether each pair of elements in the given vectors are equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The first vector to compare.</param>
+ /// <returns>True if all elements are equal; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool EqualsAll<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left == right;
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any single pair of elements in the given vectors are equal.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if any element pairs are equal; False if no element pairs are equal.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool EqualsAny<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return !Vector<T>.Equals(left, right).Equals(Vector<T>.Zero);
+ }
+ #endregion Equals methods
+
+ #region Lessthan Methods
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> LessThan<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.LessThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were less than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> LessThan(Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<int>)Vector<Single>.LessThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> LessThan(Vector<int> left, Vector<int> right)
+ {
+ return Vector<int>.LessThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were less than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> LessThan(Vector<double> left, Vector<double> right)
+ {
+ return (Vector<long>)Vector<double>.LessThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> LessThan(Vector<long> left, Vector<long> right)
+ {
+ return Vector<long>.LessThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether all of the elements in left are less than their corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if all elements in left are less than their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool LessThanAll<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.LessThan(left, right);
+ return cond.Equals(Vector<int>.AllOnes);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any element in left is less than its corresponding element in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if any elements in left are less than their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool LessThanAny<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.LessThan(left, right);
+ return !cond.Equals(Vector<int>.Zero);
+ }
+ #endregion LessthanMethods
+
+ #region Lessthanorequal methods
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> LessThanOrEqual<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.LessThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were less than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> LessThanOrEqual(Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<int>)Vector<Single>.LessThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> LessThanOrEqual(Vector<int> left, Vector<int> right)
+ {
+ return Vector<int>.LessThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were less than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> LessThanOrEqual(Vector<long> left, Vector<long> right)
+ {
+ return Vector<long>.LessThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were less than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> LessThanOrEqual(Vector<double> left, Vector<double> right)
+ {
+ return (Vector<long>)Vector<double>.LessThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether all elements in left are less than or equal to their corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if all elements in left are less than or equal to their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool LessThanOrEqualAll<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.LessThanOrEqual(left, right);
+ return cond.Equals(Vector<int>.AllOnes);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any element in left is less than or equal to its corresponding element in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if any elements in left are less than their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool LessThanOrEqualAny<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.LessThanOrEqual(left, right);
+ return !cond.Equals(Vector<int>.Zero);
+ }
+ #endregion Lessthanorequal methods
+
+ #region Greaterthan methods
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> GreaterThan<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.GreaterThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were greater than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> GreaterThan(Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<int>)Vector<Single>.GreaterThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> GreaterThan(Vector<int> left, Vector<int> right)
+ {
+ return Vector<int>.GreaterThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were greater than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> GreaterThan(Vector<double> left, Vector<double> right)
+ {
+ return (Vector<long>)Vector<double>.GreaterThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> GreaterThan(Vector<long> left, Vector<long> right)
+ {
+ return Vector<long>.GreaterThan(left, right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether all elements in left are greater than the corresponding elements in right.
+ /// elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if all elements in left are greater than their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool GreaterThanAll<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.GreaterThan(left, right);
+ return cond.Equals(Vector<int>.AllOnes);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any element in left is greater than its corresponding element in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if any elements in left are greater than their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool GreaterThanAny<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.GreaterThan(left, right);
+ return !cond.Equals(Vector<int>.Zero);
+ }
+ #endregion Greaterthan methods
+
+ #region Greaterthanorequal methods
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> GreaterThanOrEqual<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.GreaterThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were greater than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> GreaterThanOrEqual(Vector<Single> left, Vector<Single> right)
+ {
+ return (Vector<int>)Vector<Single>.GreaterThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<int> GreaterThanOrEqual(Vector<int> left, Vector<int> right)
+ {
+ return Vector<int>.GreaterThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements signal whether the elements in left were greater than or equal to their
+ /// corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> GreaterThanOrEqual(Vector<long> left, Vector<long> right)
+ {
+ return Vector<long>.GreaterThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns an integral vector whose elements signal whether the elements in left were greater than or equal to
+ /// their corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>The resultant integral vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<long> GreaterThanOrEqual(Vector<double> left, Vector<double> right)
+ {
+ return (Vector<long>)Vector<double>.GreaterThanOrEqual(left, right);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether all of the elements in left are greater than or equal to
+ /// their corresponding elements in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if all elements in left are greater than or equal to their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool GreaterThanOrEqualAll<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.GreaterThanOrEqual(left, right);
+ return cond.Equals(Vector<int>.AllOnes);
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether any element in left is greater than or equal to its corresponding element in right.
+ /// </summary>
+ /// <param name="left">The first vector to compare.</param>
+ /// <param name="right">The second vector to compare.</param>
+ /// <returns>True if any elements in left are greater than or equal to their corresponding elements in right; False otherwise.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool GreaterThanOrEqualAny<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ Vector<int> cond = (Vector<int>)Vector<T>.GreaterThanOrEqual(left, right);
+ return !cond.Equals(Vector<int>.Zero);
+ }
+ #endregion Greaterthanorequal methods
+ #endregion Comparison methods
+
+ #region Vector Math Methods
+ // Every operation must either be a JIT intrinsic or implemented over a JIT intrinsic
+ // as a thin wrapper
+ // Operations implemented over a JIT intrinsic should be inlined
+ // Methods that do not have a <T> type parameter are recognized as intrinsics
+ /// <summary>
+ /// Returns whether or not vector operations are subject to hardware acceleration through JIT intrinsic support.
+ /// </summary>
+ public static bool IsHardwareAccelerated
+ {
+ [Intrinsic]
+ get
+ {
+ return false;
+ }
+ }
+
+ // Vector<T>
+ // Basic Math
+ // All Math operations for Vector<T> are aggressively inlined here
+
+ /// <summary>
+ /// Returns a new vector whose elements are the absolute values of the given vector's elements.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The absolute value vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Abs<T>(Vector<T> value) where T : struct
+ {
+ return Vector<T>.Abs(value);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are the minimum of each pair of elements in the two given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The minimum vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Min<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.Min(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are the maximum of each pair of elements in the two given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The maximum vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Max<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.Max(left, right);
+ }
+
+ // Specialized vector operations
+
+ /// <summary>
+ /// Returns the dot product of two vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The dot product.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static T Dot<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return Vector<T>.DotProduct(left, right);
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are the square roots of the given vector's elements.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The square root vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> SquareRoot<T>(Vector<T> value) where T : struct
+ {
+ return Vector<T>.SquareRoot(value);
+ }
+ #endregion Vector Math Methods
+
+ #region Named Arithmetic Operators
+ /// <summary>
+ /// Creates a new vector whose values are the sum of each pair of elements from the two given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The summed vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Add<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left + right;
+ }
+
+ /// <summary>
+ /// Creates a new vector whose values are the difference between each pairs of elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The difference vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Subtract<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left - right;
+ }
+
+ /// <summary>
+ /// Creates a new vector whose values are the product of each pair of elements from the two given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The summed vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Multiply<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left * right;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose values are the values of the given vector each multiplied by a scalar value.
+ /// </summary>
+ /// <param name="left">The source vector.</param>
+ /// <param name="right">The scalar factor.</param>
+ /// <returns>The scaled vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Multiply<T>(Vector<T> left, T right) where T : struct
+ {
+ return left * right;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose values are the values of the given vector each multiplied by a scalar value.
+ /// </summary>
+ /// <param name="left">The scalar factor.</param>
+ /// <param name="right">The source vector.</param>
+ /// <returns>The scaled vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Multiply<T>(T left, Vector<T> right) where T : struct
+ {
+ return left * right;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose values are the result of dividing the first vector's elements
+ /// by the corresponding elements in the second vector.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The divided vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Divide<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left / right;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are the given vector's elements negated.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The negated vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Negate<T>(Vector<T> value) where T : struct
+ {
+ return -value;
+ }
+ #endregion Named Arithmetic Operators
+
+ #region Named Bitwise Operators
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-and operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> BitwiseAnd<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left & right;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> BitwiseOr<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left | right;
+ }
+
+ /// <summary>
+ /// Returns a new vector whose elements are obtained by taking the one's complement of the given vector's elements.
+ /// </summary>
+ /// <param name="value">The source vector.</param>
+ /// <returns>The one's complement vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> OnesComplement<T>(Vector<T> value) where T : struct
+ {
+ return ~value;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-exclusive-or operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Xor<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left ^ right;
+ }
+
+ /// <summary>
+ /// Returns a new vector by performing a bitwise-and-not operation on each of the elements in the given vectors.
+ /// </summary>
+ /// <param name="left">The first source vector.</param>
+ /// <param name="right">The second source vector.</param>
+ /// <returns>The resultant vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> AndNot<T>(Vector<T> left, Vector<T> right) where T : struct
+ {
+ return left & ~right;
+ }
+ #endregion Named Bitwise Operators
+
+ #region Conversion Methods
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of unsigned bytes.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Byte> AsVectorByte<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Byte>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of signed bytes.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<SByte> AsVectorSByte<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<SByte>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of 16-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<UInt16> AsVectorUInt16<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<UInt16>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of signed 16-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Int16> AsVectorInt16<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Int16>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of unsigned 32-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<UInt32> AsVectorUInt32<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<UInt32>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of signed 32-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Int32> AsVectorInt32<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Int32>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of unsigned 64-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<UInt64> AsVectorUInt64<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<UInt64>)value;
+ }
+
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of signed 64-bit integers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Int64> AsVectorInt64<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Int64>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of 32-bit floating point numbers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Single> AsVectorSingle<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Single>)value;
+ }
+
+ /// <summary>
+ /// Reinterprets the bits of the given vector into those of a vector of 64-bit floating point numbers.
+ /// </summary>
+ /// <param name="value">The source vector</param>
+ /// <returns>The reinterpreted vector.</returns>
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<Double> AsVectorDouble<T>(Vector<T> value) where T : struct
+ {
+ return (Vector<Double>)value;
+ }
+ #endregion Conversion Methods
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/OverflowException.cs b/src/System.Private.CoreLib/shared/System/OverflowException.cs
index 963825b35..e28dcb87e 100644
--- a/src/System.Private.CoreLib/shared/System/OverflowException.cs
+++ b/src/System.Private.CoreLib/shared/System/OverflowException.cs
@@ -6,7 +6,7 @@
**
**
**
-** Purpose: Exception class for Arthimatic Overflows.
+** Purpose: Exception class for Arithmetic Overflows.
**
**
=============================================================================*/
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
index 5d8f00f8a..166a204ed 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
@@ -8,7 +8,9 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
+#if !FEATURE_PORTABLE_SPAN
using Internal.Runtime.CompilerServices;
+#endif // FEATURE_PORTABLE_SPAN
namespace System
{
@@ -36,14 +38,16 @@ namespace System
/// Creates a new memory over the entirety of the target array.
/// </summary>
/// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyMemory(T[] array)
{
if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ {
+ this = default;
+ return; // returns default
+ }
_object = array;
_index = 0;
@@ -57,8 +61,7 @@ namespace System
/// <param name="array">The target array.</param>
/// <param name="start">The index at which to begin the memory.</param>
/// <param name="length">The number of items in the memory.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
@@ -67,7 +70,12 @@ namespace System
public ReadOnlyMemory(T[] array, int start, int length)
{
if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
@@ -96,7 +104,7 @@ namespace System
/// Defines an implicit conversion of an array to a <see cref="ReadOnlyMemory{T}"/>
/// </summary>
public static implicit operator ReadOnlyMemory<T>(T[] array) => new ReadOnlyMemory<T>(array);
-
+
/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlyMemory{T}"/>
/// </summary>
@@ -168,7 +176,11 @@ namespace System
}
else if (typeof(T) == typeof(char) && _object is string s)
{
+#if FEATURE_PORTABLE_SPAN
+ return new ReadOnlySpan<T>(Unsafe.As<Pinnable<T>>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
+#else
return new ReadOnlySpan<T>(ref Unsafe.As<char, T>(ref s.GetRawStringData()), s.Length).Slice(_index, _length);
+#endif // FEATURE_PORTABLE_SPAN
}
else if (_object != null)
{
@@ -216,19 +228,26 @@ namespace System
{
if (_index < 0)
{
- memoryHandle = ((OwnedMemory<T>)_object).Pin();
- memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+ memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
else if (typeof(T) == typeof(char) && _object is string s)
{
GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
+#if FEATURE_PORTABLE_SPAN
+ void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
memoryHandle = new MemoryHandle(null, pointer, handle);
}
else if (_object is T[] array)
{
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+#if FEATURE_PORTABLE_SPAN
+ void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
memoryHandle = new MemoryHandle(null, pointer, handle);
}
}
@@ -286,7 +305,7 @@ namespace System
{
return _object != null ? CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode()) : 0;
}
-
+
private static int CombineHashCodes(int left, int right)
{
return ((left << 5) + left) ^ right;
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
new file mode 100644
index 000000000..9bf3f211a
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
@@ -0,0 +1,270 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using Internal.Runtime.CompilerServices;
+
+#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
+
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ /// <summary>
+ /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
+ /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
+ /// </summary>
+ [DebuggerTypeProxy(typeof(SpanDebugView<>))]
+ [DebuggerDisplay("{ToString(),raw}")]
+ [NonVersionable]
+ public readonly ref partial struct ReadOnlySpan<T>
+ {
+ /// <summary>A byref or a native ptr.</summary>
+ internal readonly ByReference<T> _pointer;
+ /// <summary>The number of elements this ReadOnlySpan contains.</summary>
+#if PROJECTN
+ [Bound]
+#endif
+ private readonly int _length;
+
+ /// <summary>
+ /// Creates a new read-only span over the entirety of the target array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
+ /// reference (Nothing in Visual Basic).</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan(T[] array)
+ {
+ if (array == null)
+ {
+ this = default;
+ return; // returns default
+ }
+
+ _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
+ _length = array.Length;
+ }
+
+ /// <summary>
+ /// Creates a new read-only span over the portion of the target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the read-only span.</param>
+ /// <param name="length">The number of items in the read-only span.</param>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan(T[] array, int start, int length)
+ {
+ if (array == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
+ _length = length;
+ }
+
+ /// <summary>
+ /// Creates a new read-only span over the target unmanaged buffer. Clearly this
+ /// is quite dangerous, because we are creating arbitrarily typed T's
+ /// out of a void*-typed block of memory. And the length is not checked.
+ /// But if this creation is correct, then all subsequent uses are correct.
+ /// </summary>
+ /// <param name="pointer">An unmanaged pointer to memory.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
+ /// </exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="length"/> is negative.
+ /// </exception>
+ [CLSCompliant(false)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe ReadOnlySpan(void* pointer, int length)
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ if (length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
+ _length = length;
+ }
+
+ // Constructor for internal use only.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ReadOnlySpan(ref T ptr, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ _pointer = new ByReference<T>(ref ptr);
+ _length = length;
+ }
+
+ /// <summary>
+ /// Returns the specified element of the read-only span.
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ /// <exception cref="System.IndexOutOfRangeException">
+ /// Thrown when index less than 0 or index greater than or equal to Length
+ /// </exception>
+ public ref readonly T this[int index]
+ {
+#if PROJECTN
+ [BoundsChecking]
+ get
+ {
+ return ref Unsafe.Add(ref _pointer.Value, index);
+ }
+#else
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [NonVersionable]
+ get
+ {
+ if ((uint)index >= (uint)_length)
+ ThrowHelper.ThrowIndexOutOfRangeException();
+ return ref Unsafe.Add(ref _pointer.Value, index);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Copies the contents of this read-only span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ ///
+ /// <param name="destination">The span to copy items into.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when the destination Span is shorter than the source Span.
+ /// </exception>
+ /// </summary>
+ public void CopyTo(Span<T> destination)
+ {
+ // Using "if (!TryCopyTo(...))" results in two branches: one for the length
+ // check, and one for the result of TryCopyTo. Since these checks are equivalent,
+ // we can optimize by performing the check once ourselves then calling Memmove directly.
+
+ if ((uint)_length <= (uint)destination.Length)
+ {
+ Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
+ }
+ else
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+ }
+
+ /// Copies the contents of this read-only span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ /// </summary>
+ /// <returns>If the destination span is shorter than the source span, this method
+ /// return false and no data is written to the destination.</returns>
+ /// <param name="destination">The span to copy items into.</param>
+ public bool TryCopyTo(Span<T> destination)
+ {
+ bool retVal = false;
+ if ((uint)_length <= (uint)destination.Length)
+ {
+ Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /// <summary>
+ /// Returns true if left and right point at the same memory and have the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right)
+ {
+ return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
+ }
+
+ /// <summary>
+ /// For <see cref="ReadOnlySpan{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
+ /// Otherwise, returns a <see cref="String"/> with the name of the type and the number of elements.
+ /// </summary>
+ public override string ToString()
+ {
+ if (typeof(T) == typeof(char))
+ {
+ unsafe
+ {
+ fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
+ return new string(src, 0, _length);
+ }
+ }
+ return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given read-only span, beginning at 'start'.
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan<T> Slice(int start)
+ {
+ if ((uint)start > (uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given read-only span, beginning at 'start', of given length
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan<T> Slice(int start, int length)
+ {
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
+ }
+
+ /// <summary>
+ /// Copies the contents of this read-only span into a new array. This heap
+ /// allocates, so should generally be avoided, however it is sometimes
+ /// necessary to bridge the gap with APIs written in terms of arrays.
+ /// </summary>
+ public T[] ToArray()
+ {
+ if (_length == 0)
+ return Array.Empty<T>();
+
+ var destination = new T[_length];
+ Buffer.Memmove(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length);
+ return destination;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs
index c49dddf98..906a3c431 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs
@@ -5,8 +5,9 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+#if !FEATURE_PORTABLE_SPAN
using System.Runtime.Versioning;
-using Internal.Runtime.CompilerServices;
+#endif // !FEATURE_PORTABLE_SPAN
#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
@@ -17,128 +18,17 @@ namespace System
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
[DebuggerTypeProxy(typeof(SpanDebugView<>))]
- [DebuggerDisplay("{DebuggerDisplay,nq}")]
- [NonVersionable]
- public readonly ref struct ReadOnlySpan<T>
+ [DebuggerDisplay("{ToString(),raw}")]
+ public readonly ref partial struct ReadOnlySpan<T>
{
- /// <summary>A byref or a native ptr.</summary>
- internal readonly ByReference<T> _pointer;
- /// <summary>The number of elements this ReadOnlySpan contains.</summary>
-#if PROJECTN
- [Bound]
-#endif
- private readonly int _length;
-
- /// <summary>
- /// Creates a new read-only span over the entirety of the target array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
- _length = array.Length;
- }
-
- /// <summary>
- /// Creates a new read-only span over the portion of the target array beginning
- /// at 'start' index and ending at 'end' index (exclusive).
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the read-only span.</param>
- /// <param name="length">The number of items in the read-only span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array, int start, int length)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
- _length = length;
- }
-
- /// <summary>
- /// Creates a new read-only span over the target unmanaged buffer. Clearly this
- /// is quite dangerous, because we are creating arbitrarily typed T's
- /// out of a void*-typed block of memory. And the length is not checked.
- /// But if this creation is correct, then all subsequent uses are correct.
- /// </summary>
- /// <param name="pointer">An unmanaged pointer to memory.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
- /// </exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="length"/> is negative.
- /// </exception>
- [CLSCompliant(false)]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe ReadOnlySpan(void* pointer, int length)
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
- if (length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
- _length = length;
- }
-
- /// <summary>
- /// Create a new read-only span over a portion of a regular managed object. This can be useful
- /// if part of a managed object represents a "fixed array." This is dangerous because neither the
- /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
- /// "rawPointer" actually lies within <paramref name="obj"/>.
- /// </summary>
- /// <param name="obj">The managed object that contains the data to span over.</param>
- /// <param name="objectData">A reference to data within that object.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length);
-
- // Constructor for internal use only.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan(ref T ptr, int length)
- {
- Debug.Assert(length >= 0);
-
- _pointer = new ByReference<T>(ref ptr);
- _length = length;
- }
-
- //Debugger Display = {T[length]}
- private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length);
-
- /// <summary>
- /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
- /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal ref readonly T DangerousGetPinnableReference()
- {
- return ref _pointer.Value;
- }
-
/// <summary>
/// The number of items in the read-only span.
/// </summary>
public int Length
{
+#if !FEATURE_PORTABLE_SPAN
[NonVersionable]
+#endif // !FEATURE_PORTABLE_SPAN
get
{
return _length;
@@ -150,83 +40,14 @@ namespace System
/// </summary>
public bool IsEmpty
{
+#if !FEATURE_PORTABLE_SPAN
[NonVersionable]
+#endif // !FEATURE_PORTABLE_SPAN
get
{
return _length == 0;
}
}
-
- /// <summary>
- /// Returns the specified element of the read-only span.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- /// <exception cref="System.IndexOutOfRangeException">
- /// Thrown when index less than 0 or index greater than or equal to Length
- /// </exception>
- public ref readonly T this[int index]
- {
-#if PROJECTN
- [BoundsChecking]
- get
- {
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
-#else
- [Intrinsic]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [NonVersionable]
- get
- {
- if ((uint)index >= (uint)_length)
- ThrowHelper.ThrowIndexOutOfRangeException();
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
-#endif
- }
-
- /// <summary>
- /// Copies the contents of this read-only span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- ///
- /// <param name="destination">The span to copy items into.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when the destination Span is shorter than the source Span.
- /// </exception>
- /// </summary>
- public void CopyTo(Span<T> destination)
- {
- if (!TryCopyTo(destination))
- ThrowHelper.ThrowArgumentException_DestinationTooShort();
- }
-
- /// Copies the contents of this read-only span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <returns>If the destination span is shorter than the source span, this method
- /// return false and no data is written to the destination.</returns>
- /// <param name="destination">The span to copy items into.</param>
- public bool TryCopyTo(Span<T> destination)
- {
- if ((uint)_length > (uint)destination.Length)
- return false;
-
- Span.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length);
- return true;
- }
-
- /// <summary>
- /// Returns true if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right)
- {
- return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
- }
-
/// <summary>
/// Returns false if left and right point at the same memory and have the same length. Note that
/// this does *not* check to see if the *contents* are equal.
@@ -239,7 +60,7 @@ namespace System
/// Always thrown by this method.
/// </exception>
/// </summary>
- [Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
+ [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj)
{
@@ -252,7 +73,7 @@ namespace System
/// Always thrown by this method.
/// </exception>
/// </summary>
- [Obsolete("GetHashCode() on Span will always throw an exception.")]
+ [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
@@ -262,61 +83,13 @@ namespace System
/// <summary>
/// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/>
/// </summary>
- public static implicit operator ReadOnlySpan<T>(T[] array) => array != null ? new ReadOnlySpan<T>(array) : default;
+ public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/>
/// </summary>
public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment)
- => arraySegment.Array != null ? new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default;
-
- /// <summary>
- /// Forms a slice out of the given read-only span, beginning at 'start'.
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan<T> Slice(int start)
- {
- if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
- }
-
- /// <summary>
- /// Forms a slice out of the given read-only span, beginning at 'start', of given length
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The desired length for the slice (exclusive).</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan<T> Slice(int start, int length)
- {
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
- }
-
- /// <summary>
- /// Copies the contents of this read-only span into a new array. This heap
- /// allocates, so should generally be avoided, however it is sometimes
- /// necessary to bridge the gap with APIs written in terms of arrays.
- /// </summary>
- public T[] ToArray()
- {
- if (_length == 0)
- return Array.Empty<T>();
-
- var destination = new T[_length];
- Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length);
- return destination;
- }
+ => new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
/// <summary>
/// Returns a 0-length read-only span whose base is the null pointer.
diff --git a/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs b/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs
index 7c4a98007..716afb045 100644
--- a/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs
+++ b/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs
@@ -95,7 +95,7 @@ namespace System.Reflection
//@todo: App-compat: You can use double or single quotes to quote a name, and Fusion (or rather the IdentityAuthority) picks one
// by some algorithm. Rather than guess at it, I'll just use double-quote consistently.
- if (s != s.Trim() || s.Contains("\"") || s.Contains("\'"))
+ if (s != s.Trim() || s.Contains('\"') || s.Contains('\''))
needsQuoting = true;
if (needsQuoting)
diff --git a/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs b/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs
index d78c06895..5011c5005 100644
--- a/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs
+++ b/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs
@@ -27,7 +27,7 @@ namespace System.Reflection
HResult = HResults.COR_E_REFLECTIONTYPELOAD;
}
- private ReflectionTypeLoadException(SerializationInfo info, StreamingContext context)
+ private ReflectionTypeLoadException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
LoaderExceptions = (Exception[])(info.GetValue("Exceptions", typeof(Exception[])));
@@ -40,48 +40,34 @@ namespace System.Reflection
info.AddValue("Exceptions", LoaderExceptions, typeof(Exception[]));
}
- public override string Message
+ public Type[] Types { get; }
+
+ public Exception[] LoaderExceptions { get; }
+
+ public override string Message => CreateString(isMessage: true);
+
+ public override string ToString() => CreateString(isMessage: false);
+
+ private string CreateString(bool isMessage)
{
- get
- {
- if (LoaderExceptions == null || LoaderExceptions.Length == 0)
- {
- return base.Message;
- }
+ string baseValue = isMessage ? base.Message : base.ToString();
- StringBuilder text = new StringBuilder();
- text.AppendLine(base.Message);
- foreach (Exception e in LoaderExceptions)
- {
- if (e != null)
- {
- text.AppendLine(e.Message);
- }
- }
- return text.ToString();
+ Exception[] exceptions = LoaderExceptions;
+ if (exceptions == null || exceptions.Length == 0)
+ {
+ return baseValue;
}
- }
- public override string ToString()
- {
- StringBuilder text = new StringBuilder();
- text.AppendLine(base.ToString());
- if (LoaderExceptions != null)
+ var text = new StringBuilder(baseValue);
+ foreach (Exception e in exceptions)
{
- foreach (Exception e in LoaderExceptions)
+ if (e != null)
{
- if (e != null)
- {
- text.AppendLine(e.ToString());
- }
+ text.AppendLine();
+ text.Append(isMessage ? e.Message : e.ToString());
}
}
-
return text.ToString();
}
-
- public Type[] Types { get; }
-
- public Exception[] LoaderExceptions { get; }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs b/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs
index 5847944f1..924713254 100644
--- a/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs
+++ b/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs
@@ -9,6 +9,7 @@ using System.Diagnostics;
namespace System.Reflection
{
#if CORERT
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public // Needs to be public so that Reflection.Core can see it.
#else
internal
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs
index b5ecd7924..0e1220d11 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs
@@ -8,6 +8,108 @@ using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
+ /// <summary>Represents a builder for asynchronous methods that return a <see cref="ValueTask"/>.</summary>
+ [StructLayout(LayoutKind.Auto)]
+ public struct AsyncValueTaskMethodBuilder
+ {
+ /// <summary>The <see cref="AsyncTaskMethodBuilder"/> to which most operations are delegated.</summary>
+ private AsyncTaskMethodBuilder _methodBuilder; // mutable struct; do not make it readonly
+ /// <summary>true if completed synchronously and successfully; otherwise, false.</summary>
+ private bool _haveResult;
+ /// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary>
+ private bool _useBuilder;
+
+ /// <summary>Creates an instance of the <see cref="AsyncValueTaskMethodBuilder"/> struct.</summary>
+ /// <returns>The initialized instance.</returns>
+ public static AsyncValueTaskMethodBuilder Create() =>
+#if CORERT
+ // corert's AsyncTaskMethodBuilder.Create() currently does additional debugger-related
+ // work, so we need to delegate to it.
+ new AsyncValueTaskMethodBuilder() { _methodBuilder = AsyncTaskMethodBuilder.Create() };
+#else
+ // _methodBuilder should be initialized to AsyncTaskMethodBuilder.Create(), but on coreclr
+ // that Create() is a nop, so we can just return the default here.
+ default;
+#endif
+
+ /// <summary>Begins running the builder with the associated state machine.</summary>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="stateMachine">The state machine instance, passed by reference.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
+ // will provide the right ExecutionContext semantics
+#if netstandard
+ _methodBuilder.Start(ref stateMachine);
+#else
+ AsyncMethodBuilderCore.Start(ref stateMachine);
+#endif
+
+ /// <summary>Associates the builder with the specified state machine.</summary>
+ /// <param name="stateMachine">The state machine instance to associate with the builder.</param>
+ public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine);
+
+ /// <summary>Marks the task as successfully completed.</summary>
+ public void SetResult()
+ {
+ if (_useBuilder)
+ {
+ _methodBuilder.SetResult();
+ }
+ else
+ {
+ _haveResult = true;
+ }
+ }
+
+ /// <summary>Marks the task as failed and binds the specified exception to the task.</summary>
+ /// <param name="exception">The exception to bind to the task.</param>
+ public void SetException(Exception exception) => _methodBuilder.SetException(exception);
+
+ /// <summary>Gets the task for this builder.</summary>
+ public ValueTask Task
+ {
+ get
+ {
+ if (_haveResult)
+ {
+ return default;
+ }
+ else
+ {
+ _useBuilder = true;
+ return new ValueTask(_methodBuilder.Task);
+ }
+ }
+ }
+
+ /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
+ /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="awaiter">The awaiter.</param>
+ /// <param name="stateMachine">The state machine.</param>
+ public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : INotifyCompletion
+ where TStateMachine : IAsyncStateMachine
+ {
+ _useBuilder = true;
+ _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
+ }
+
+ /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
+ /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="awaiter">The awaiter.</param>
+ /// <param name="stateMachine">The state machine.</param>
+ [SecuritySafeCritical]
+ public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : ICriticalNotifyCompletion
+ where TStateMachine : IAsyncStateMachine
+ {
+ _useBuilder = true;
+ _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
+ }
+ }
+
/// <summary>Represents a builder for asynchronous methods that returns a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
[StructLayout(LayoutKind.Auto)]
@@ -32,14 +134,20 @@ namespace System.Runtime.CompilerServices
#else
// _methodBuilder should be initialized to AsyncTaskMethodBuilder<TResult>.Create(), but on coreclr
// that Create() is a nop, so we can just return the default here.
- default(AsyncValueTaskMethodBuilder<TResult>);
+ default;
#endif
/// <summary>Begins running the builder with the associated state machine.</summary>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="stateMachine">The state machine instance, passed by reference.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
- _methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics
+ // will provide the right ExecutionContext semantics
+#if netstandard
+ _methodBuilder.Start(ref stateMachine);
+#else
+ AsyncMethodBuilderCore.Start(ref stateMachine);
+#endif
/// <summary>Associates the builder with the specified state machine.</summary>
/// <param name="stateMachine">The state machine instance to associate with the builder.</param>
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
index 4e8ce691b..b6f12299b 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
@@ -5,9 +5,115 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
+using System.Threading.Tasks.Sources;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
namespace System.Runtime.CompilerServices
{
+ /// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask"/>.</summary>
+ [StructLayout(LayoutKind.Auto)]
+ public readonly struct ConfiguredValueTaskAwaitable
+ {
+ /// <summary>The wrapped <see cref="Task"/>.</summary>
+ private readonly ValueTask _value;
+
+ /// <summary>Initializes the awaitable.</summary>
+ /// <param name="value">The wrapped <see cref="ValueTask"/>.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ConfiguredValueTaskAwaitable(ValueTask value) => _value = value;
+
+ /// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable"/> instance.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value);
+
+ /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
+ [StructLayout(LayoutKind.Auto)]
+ public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
+#if CORECLR
+ , IValueTaskAwaiter
+#endif
+ {
+ /// <summary>The value being awaited.</summary>
+ private readonly ValueTask _value;
+
+ /// <summary>Initializes the awaiter.</summary>
+ /// <param name="value">The value to be awaited.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ConfiguredValueTaskAwaiter(ValueTask value) => _value = value;
+
+ /// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable"/> has completed.</summary>
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _value.IsCompleted;
+ }
+
+ /// <summary>Gets the result of the ValueTask.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [StackTraceHidden]
+ public void GetResult() => _value.ThrowIfCompletedUnsuccessfully();
+
+ /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
+ public void OnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
+ ValueTaskSourceOnCompletedFlags.FlowExecutionContext |
+ (_value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
+ }
+ else
+ {
+ ValueTask.CompletedTask.ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+ }
+ }
+
+ /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
+ _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
+ }
+ else
+ {
+ ValueTask.CompletedTask.ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ }
+
+#if CORECLR
+ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
+ {
+ if (_value.ObjectIsTask)
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(_value.UnsafeGetTask(), box, _value.ContinueOnCapturedContext);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
+ _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
+ }
+ else
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value.ContinueOnCapturedContext);
+ }
+ }
+#endif
+ }
+ }
+
/// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result produced.</typeparam>
[StructLayout(LayoutKind.Auto)]
@@ -15,74 +121,98 @@ namespace System.Runtime.CompilerServices
{
/// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary>
private readonly ValueTask<TResult> _value;
- /// <summary>true to attempt to marshal the continuation back to the original context captured; otherwise, false.</summary>
- private readonly bool _continueOnCapturedContext;
/// <summary>Initializes the awaitable.</summary>
/// <param name="value">The wrapped <see cref="ValueTask{TResult}"/>.</param>
- /// <param name="continueOnCapturedContext">
- /// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false.
- /// </param>
- internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value, bool continueOnCapturedContext)
- {
- _value = value;
- _continueOnCapturedContext = continueOnCapturedContext;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value) => _value = value;
/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary>
- public ConfiguredValueTaskAwaiter GetAwaiter() =>
- new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value);
/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
[StructLayout(LayoutKind.Auto)]
- public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IConfiguredValueTaskAwaiter
+ public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
+#if CORECLR
+ , IValueTaskAwaiter
+#endif
{
/// <summary>The value being awaited.</summary>
- private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
- /// <summary>The value to pass to ConfigureAwait.</summary>
- internal readonly bool _continueOnCapturedContext;
+ private readonly ValueTask<TResult> _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
- /// <param name="continueOnCapturedContext">The value to pass to ConfigureAwait.</param>
- internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext)
- {
- _value = value;
- _continueOnCapturedContext = continueOnCapturedContext;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
/// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable{TResult}"/> has completed.</summary>
- public bool IsCompleted => _value.IsCompleted;
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _value.IsCompleted;
+ }
/// <summary>Gets the result of the ValueTask.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
- public TResult GetResult() =>
- _value._task == null ?
- _value._result :
- _value._task.GetAwaiter().GetResult();
+ public TResult GetResult() => _value.Result;
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
- public void OnCompleted(Action continuation) =>
- _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+ public void OnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
+ ValueTaskSourceOnCompletedFlags.FlowExecutionContext |
+ (_value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
+ }
+ else
+ {
+ ValueTask.CompletedTask.ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+ }
+ }
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
- public void UnsafeOnCompleted(Action continuation) =>
- _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
-
- /// <summary>Gets the task underlying <see cref="_value"/>.</summary>
- internal Task<TResult> AsTask() => _value.AsTask();
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
+ _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
+ }
+ else
+ {
+ ValueTask.CompletedTask.ConfigureAwait(_value.ContinueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ }
- /// <summary>Gets the task underlying the incomplete <see cref="_value"/>.</summary>
- /// <remarks>This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task.</remarks>
- (Task task, bool continueOnCapturedContext) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext);
+#if CORECLR
+ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
+ {
+ if (_value.ObjectIsTask)
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(_value.UnsafeGetTask(), box, _value.ContinueOnCapturedContext);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
+ _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
+ }
+ else
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value.ContinueOnCapturedContext);
+ }
+ }
+#endif
}
}
-
- /// <summary>
- /// Internal interface used to enable extract the Task from arbitrary configured ValueTask awaiters.
- /// </summary>
- internal interface IConfiguredValueTaskAwaiter
- {
- (Task task, bool continueOnCapturedContext) GetTask();
- }
-}
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs
index 381b4c63f..6bdd91d84 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs
@@ -6,8 +6,8 @@ namespace System.Runtime.CompilerServices
{
// Calls to methods or references to fields marked with this attribute may be replaced at
// some call sites with jit intrinsic expansions.
- // Types marked with this attribute may be specially treated by the rumtime/compiler.
- [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
+ // Types marked with this attribute may be specially treated by the runtime/compiler.
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
internal sealed class IntrinsicAttribute : Attribute
{
}
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs
index c4a855824..27dd64575 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs
@@ -9,6 +9,9 @@ namespace System.Runtime.CompilerServices
{
public TypeForwardedFromAttribute(string assemblyFullName)
{
+ if (string.IsNullOrEmpty(assemblyFullName))
+ throw new ArgumentNullException(nameof(assemblyFullName));
+
AssemblyFullName = assemblyFullName;
}
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
index 7bc8b5cc7..221a1a437 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
@@ -4,50 +4,198 @@
using System.Diagnostics;
using System.Threading.Tasks;
+using System.Threading.Tasks.Sources;
namespace System.Runtime.CompilerServices
{
+ /// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
+ public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion
+#if CORECLR
+ , IValueTaskAwaiter
+#endif
+ {
+ /// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
+ internal static readonly Action<object> s_invokeActionDelegate = state =>
+ {
+ if (!(state is Action action))
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
+ return;
+ }
+
+ action();
+ };
+ /// <summary>The value being awaited.</summary>
+ private readonly ValueTask _value;
+
+ /// <summary>Initializes the awaiter.</summary>
+ /// <param name="value">The value to be awaited.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ValueTaskAwaiter(ValueTask value) => _value = value;
+
+ /// <summary>Gets whether the <see cref="ValueTask"/> has completed.</summary>
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _value.IsCompleted;
+ }
+
+ /// <summary>Gets the result of the ValueTask.</summary>
+ [StackTraceHidden]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void GetResult() => _value.ThrowIfCompletedUnsuccessfully();
+
+ /// <summary>Schedules the continuation action for this ValueTask.</summary>
+ public void OnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().GetAwaiter().OnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
+ }
+ else
+ {
+ ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
+ }
+ }
+
+ /// <summary>Schedules the continuation action for this ValueTask.</summary>
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
+ }
+ else
+ {
+ ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ }
+
+#if CORECLR
+ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
+ {
+ if (_value.ObjectIsTask)
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(_value.UnsafeGetTask(), box, continueOnCapturedContext: true);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
+ }
+ else
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
+ }
+ }
+
+ /// <summary>Shim used to invoke <see cref="ITaskCompletionAction.Invoke"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
+ internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
+ {
+ if (!(state is IAsyncStateMachineBox box))
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
+ return;
+ }
+
+ box.Invoke(null);
+ };
+#endif
+ }
+
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
- public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion, IValueTaskAwaiter
+ public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
+#if CORECLR
+ , IValueTaskAwaiter
+#endif
{
/// <summary>The value being awaited.</summary>
- private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
+ private readonly ValueTask<TResult> _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
- public bool IsCompleted => _value.IsCompleted;
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _value.IsCompleted;
+ }
/// <summary>Gets the result of the ValueTask.</summary>
[StackTraceHidden]
- public TResult GetResult() =>
- _value._task == null ?
- _value._result :
- _value._task.GetAwaiter().GetResult();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TResult GetResult() => _value.Result;
/// <summary>Schedules the continuation action for this ValueTask.</summary>
- public void OnCompleted(Action continuation) =>
- _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation);
+ public void OnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().GetAwaiter().OnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
+ }
+ else
+ {
+ ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
+ }
+ }
/// <summary>Schedules the continuation action for this ValueTask.</summary>
- public void UnsafeOnCompleted(Action continuation) =>
- _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation);
-
- /// <summary>Gets the task underlying <see cref="_value"/>.</summary>
- internal Task<TResult> AsTask() => _value.AsTask();
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ if (_value.ObjectIsTask)
+ {
+ _value.UnsafeGetTask().GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
+ }
+ else
+ {
+ ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ }
- /// <summary>Gets the task underlying the incomplete <see cref="_value"/>.</summary>
- /// <remarks>This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task.</remarks>
- Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull();
+#if CORECLR
+ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
+ {
+ if (_value.ObjectIsTask)
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(_value.UnsafeGetTask(), box, continueOnCapturedContext: true);
+ }
+ else if (_value._obj != null)
+ {
+ _value.UnsafeGetValueTaskSource().OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
+ }
+ else
+ {
+ TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
+ }
+ }
+#endif
}
- /// <summary>
- /// Internal interface used to enable extract the Task from arbitrary ValueTask awaiters.
- /// </summary>>
+#if CORECLR
+ /// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/> on <see cref="ValueTask"/>.</summary>>
internal interface IValueTaskAwaiter
{
- Task GetTask();
+ /// <summary>Invoked to set <see cref="ITaskCompletionAction.Invoke"/> of the <paramref name="box"/> as the awaiter's continuation.</summary>
+ /// <param name="box">The box object.</param>
+ void AwaitUnsafeOnCompleted(IAsyncStateMachineBox box);
}
-}
+#endif
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs
index bcd1f616b..c45ef2484 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs
@@ -5,7 +5,6 @@
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
//
-
//
// Compiler-targeted type for switching back into the current execution context, e.g.
//
@@ -25,6 +24,7 @@
using System;
using System.Security;
using System.Diagnostics;
+using System.Diagnostics.Tracing;
using System.Threading;
using System.Threading.Tasks;
@@ -46,7 +46,7 @@ namespace System.Runtime.CompilerServices
/// <summary>Provides an awaiter that switches into a target environment.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
- public struct YieldAwaiter : ICriticalNotifyCompletion
+ public readonly struct YieldAwaiter : ICriticalNotifyCompletion
{
/// <summary>Gets whether a yield is not required.</summary>
/// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
@@ -57,7 +57,7 @@ namespace System.Runtime.CompilerServices
/// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
public void OnCompleted(Action continuation)
{
- QueueContinuation(continuation);
+ QueueContinuation(continuation, flowContext: true);
}
/// <summary>Posts the <paramref name="continuation"/> back to the current context.</summary>
@@ -65,17 +65,22 @@ namespace System.Runtime.CompilerServices
/// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
public void UnsafeOnCompleted(Action continuation)
{
- QueueContinuation(continuation);
+ QueueContinuation(continuation, flowContext: false);
}
/// <summary>Posts the <paramref name="continuation"/> back to the current context.</summary>
/// <param name="continuation">The action to invoke asynchronously.</param>
+ /// <param name="flowContext">true to flow ExecutionContext; false if flowing is not required.</param>
/// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
- private static void QueueContinuation(Action continuation)
+ private static void QueueContinuation(Action continuation, bool flowContext)
{
// Validate arguments
if (continuation == null) throw new ArgumentNullException(nameof(continuation));
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ continuation = OutputCorrelationEtwEvent(continuation);
+ }
// Get the current SynchronizationContext, and if there is one,
// post the continuation to it. However, treat the base type
// as if there wasn't a SynchronizationContext, since that's what it
@@ -93,7 +98,14 @@ namespace System.Runtime.CompilerServices
TaskScheduler scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
- ThreadPool.UnsafeQueueUserWorkItem(s_waitCallbackRunAction, continuation);
+ if (flowContext)
+ {
+ ThreadPool.QueueUserWorkItem(s_waitCallbackRunAction, continuation);
+ }
+ else
+ {
+ ThreadPool.UnsafeQueueUserWorkItem(s_waitCallbackRunAction, continuation);
+ }
}
// We're targeting a custom scheduler, so queue a task.
else
@@ -103,6 +115,40 @@ namespace System.Runtime.CompilerServices
}
}
+ private static Action OutputCorrelationEtwEvent(Action continuation)
+ {
+#if CORERT
+ // TODO
+ return continuation;
+#else
+ int continuationId = Task.NewId();
+ Task currentTask = Task.InternalCurrent;
+ // fire the correlation ETW event
+ TplEtwProvider.Log.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, (currentTask != null) ? currentTask.Id : 0, continuationId);
+
+ return AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, (innerContinuation,continuationIdTask) =>
+ {
+ var etwLog = TplEtwProvider.Log;
+ etwLog.TaskWaitContinuationStarted(((Task<int>)continuationIdTask).Result);
+
+ // ETW event for Task Wait End.
+ Guid prevActivityId = new Guid();
+ // Ensure the continuation runs under the correlated activity ID generated above
+ if (etwLog.TasksSetActivityIds)
+ EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(((Task<int>)continuationIdTask).Result), out prevActivityId);
+
+ // Invoke the original continuation provided to OnCompleted.
+ innerContinuation();
+ // Restore activity ID
+
+ if (etwLog.TasksSetActivityIds)
+ EventSource.SetCurrentThreadActivityId(prevActivityId);
+
+ etwLog.TaskWaitContinuationComplete(((Task<int>)continuationIdTask).Result);
+ }, Task.FromResult(continuationId)); // pass the ID in a task to avoid a closure\
+#endif
+ }
+
/// <summary>WaitCallback that invokes the Action supplied as object state.</summary>
private static readonly WaitCallback s_waitCallbackRunAction = RunAction;
/// <summary>SendOrPostCallback that invokes the Action supplied as object state.</summary>
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
new file mode 100644
index 000000000..8a5bb7539
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
@@ -0,0 +1,170 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Collections.Generic;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>,
+ /// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>.
+ /// </summary>
+ public static partial class MemoryMarshal
+ {
+ /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
+ /// <param name="readOnlyMemory">The <see cref="ReadOnlyMemory{T}"/>.</param>
+ /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns>
+ /// <remarks>
+ /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution. <see cref="ReadOnlyMemory{T}"/> is used
+ /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created
+ /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to. The method exists to enable variables typed
+ /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>.
+ /// </remarks>
+ public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> readOnlyMemory) =>
+ Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref readOnlyMemory);
+
+ /// <summary>
+ /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
+ /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
+ /// </summary>
+ public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value;
+
+ /// <summary>
+ /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element
+ /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
+ /// </summary>
+ public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value;
+
+ /// <summary>
+ /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
+ /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <remarks>
+ /// Supported only for platforms that support misaligned memory access.
+ /// </remarks>
+ /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
+ /// </exception>
+ public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> source)
+ where TFrom : struct
+ where TTo : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
+
+ // Use unsigned integers - unsigned division by constant (especially by power of 2)
+ // and checked casts are faster and smaller.
+ uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
+ uint toSize = (uint)Unsafe.SizeOf<TTo>();
+ uint fromLength = (uint)source.Length;
+ int toLength;
+ if (fromSize == toSize)
+ {
+ // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // should be optimized to just `length` but the JIT doesn't do that today.
+ toLength = (int)fromLength;
+ }
+ else if (fromSize == 1)
+ {
+ // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
+ // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
+ // the JIT can't eliminate long multiply by 1.
+ toLength = (int)(fromLength / toSize);
+ }
+ else
+ {
+ // Ensure that casts are done in such a way that the JIT is able to "see"
+ // the uint->ulong casts and the multiply together so that on 32 bit targets
+ // 32x32to64 multiplication is used.
+ ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
+ toLength = checked((int)toLengthUInt64);
+ }
+
+ return new Span<TTo>(
+ ref Unsafe.As<TFrom, TTo>(ref source._pointer.Value),
+ toLength);
+ }
+
+ /// <summary>
+ /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
+ /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <remarks>
+ /// Supported only for platforms that support misaligned memory access.
+ /// </remarks>
+ /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
+ /// </exception>
+ public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> source)
+ where TFrom : struct
+ where TTo : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
+
+ // Use unsigned integers - unsigned division by constant (especially by power of 2)
+ // and checked casts are faster and smaller.
+ uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
+ uint toSize = (uint)Unsafe.SizeOf<TTo>();
+ uint fromLength = (uint)source.Length;
+ int toLength;
+ if (fromSize == toSize)
+ {
+ // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // should be optimized to just `length` but the JIT doesn't do that today.
+ toLength = (int)fromLength;
+ }
+ else if (fromSize == 1)
+ {
+ // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
+ // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
+ // the JIT can't eliminate long multiply by 1.
+ toLength = (int)(fromLength / toSize);
+ }
+ else
+ {
+ // Ensure that casts are done in such a way that the JIT is able to "see"
+ // the uint->ulong casts and the multiply together so that on 32 bit targets
+ // 32x32to64 multiplication is used.
+ ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
+ toLength = checked((int)toLengthUInt64);
+ }
+
+ return new ReadOnlySpan<TTo>(
+ ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)),
+ toLength);
+ }
+
+ /// <summary>
+ /// Create a new span over a portion of a regular managed object. This can be useful
+ /// if part of a managed object represents a "fixed array." This is dangerous because the
+ /// <paramref name="length"/> is not checked.
+ /// </summary>
+ /// <param name="reference">A reference to data.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> CreateSpan<T>(ref T reference, int length) => new Span<T>(ref reference, length);
+
+ /// <summary>
+ /// Create a new read-only span over a portion of a regular managed object. This can be useful
+ /// if part of a managed object represents a "fixed array." This is dangerous because the
+ /// <paramref name="length"/> is not checked.
+ /// </summary>
+ /// <param name="reference">A reference to data.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) => new ReadOnlySpan<T>(ref reference, length);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
index 2651df53a..316ce12aa 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
@@ -4,7 +4,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
-using Internal.Runtime.CompilerServices;
+using System.Collections.Generic;
namespace System.Runtime.InteropServices
{
@@ -12,24 +12,12 @@ namespace System.Runtime.InteropServices
/// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>,
/// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>.
/// </summary>
- public static class MemoryMarshal
+ public static partial class MemoryMarshal
{
- /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
- /// <param name="readOnlyMemory">The <see cref="ReadOnlyMemory{T}"/>.</param>
- /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns>
- /// <remarks>
- /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution. <see cref="ReadOnlyMemory{T}"/> is used
- /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created
- /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to. The method exists to enable variables typed
- /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>.
- /// </remarks>
- public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> readOnlyMemory) =>
- Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref readOnlyMemory);
-
- public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value;
-
- public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value;
-
+ /// <summary>
+ /// Get an array segment from the underlying memory.
+ /// If unable to get the array segment, return false with a default array segment.
+ /// </summary>
public static bool TryGetArray<T>(ReadOnlyMemory<T> readOnlyMemory, out ArraySegment<T> arraySegment)
{
object obj = readOnlyMemory.GetObjectStartLength(out int index, out int length);
@@ -47,8 +35,92 @@ namespace System.Runtime.InteropServices
return true;
}
+ if (length == 0)
+ {
+#if FEATURE_PORTABLE_SPAN
+ arraySegment = new ArraySegment<T>(SpanHelpers.PerTypeValues<T>.EmptyArray);
+#else
+ arraySegment = ArraySegment<T>.Empty;
+#endif // FEATURE_PORTABLE_SPAN
+ return true;
+ }
+
arraySegment = default;
return false;
}
+
+ /// <summary>
+ /// Gets an <see cref="OwnedMemory{T}"/> from the underlying readOnlyMemory.
+ /// If unable to get the <typeparamref name="TOwner"/> type, returns false.
+ /// </summary>
+ /// <typeparam name="T">The element type of the <paramref name="readOnlyMemory" />.</typeparam>
+ /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam>
+ /// <param name="readOnlyMemory">The memory to get the owner for.</param>
+ /// <param name="ownedMemory">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param>
+ /// <returns>A <see cref="bool"/> indicating if it was successful.</returns>
+ public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> readOnlyMemory, out TOwner ownedMemory)
+ where TOwner : OwnedMemory<T>
+ {
+ TOwner owner; // Use register for null comparison rather than byref
+ ownedMemory = owner = readOnlyMemory.GetObjectStartLength(out int index, out int length) as TOwner;
+ return !ReferenceEquals(owner, null);
+ }
+
+ /// <summary>
+ /// Gets an <see cref="OwnedMemory{T}"/> and <paramref name="index" />, <paramref name="length" /> from the underlying memory.
+ /// If unable to get the <typeparamref name="TOwner"/> type, returns false.
+ /// </summary>
+ /// <typeparam name="T">The element type of the <paramref name="readOnlyMemory" />.</typeparam>
+ /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam>
+ /// <param name="readOnlyMemory">The memory to get the owner for.</param>
+ /// <param name="ownedMemory">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param>
+ /// <param name="index">The offset from the start of the <paramref name="ownedMemory" /> that the <paramref name="readOnlyMemory" /> represents.</param>
+ /// <param name="length">The length of the <paramref name="ownedMemory" /> that the <paramref name="readOnlyMemory" /> represents.</param>
+ /// <returns>A <see cref="bool"/> indicating if it was successful.</returns>
+ public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> readOnlyMemory, out TOwner ownedMemory, out int index, out int length)
+ where TOwner : OwnedMemory<T>
+ {
+ TOwner owner; // Use register for null comparison rather than byref
+ ownedMemory = owner = readOnlyMemory.GetObjectStartLength(out index, out length) as TOwner;
+ index &= ReadOnlyMemory<T>.RemoveOwnedFlagBitMask;
+ return !ReferenceEquals(owner, null);
+ }
+
+ /// <summary>
+ /// Creates an <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /> to allow
+ /// the <paramref name="memory" /> to be used in existing APIs that take an <see cref="IEnumerable{T}"/>.
+ /// </summary>
+ /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam>
+ /// <param name="memory">The ReadOnlyMemory to view as an <see cref="IEnumerable{T}"/></param>
+ /// <returns>An <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /></returns>
+ public static IEnumerable<T> ToEnumerable<T>(ReadOnlyMemory<T> memory)
+ {
+ for (int i = 0; i < memory.Length; i++)
+ yield return memory.Span[i];
+ }
+
+ /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
+ /// <param name="readOnlyMemory">The memory that may be wrapping a <see cref="string"/> object.</param>
+ /// <param name="text">The string.</param>
+ /// <param name="start">The starting location in <paramref name="text"/>.</param>
+ /// <param name="length">The number of items in <paramref name="text"/>.</param>
+ /// <returns></returns>
+ public static bool TryGetString(ReadOnlyMemory<char> readOnlyMemory, out string text, out int start, out int length)
+ {
+ if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s)
+ {
+ text = s;
+ start = offset;
+ length = count;
+ return true;
+ }
+ else
+ {
+ text = null;
+ start = 0;
+ length = 0;
+ return false;
+ }
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StringBuffer.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StringBuffer.cs
deleted file mode 100644
index fdd0b9559..000000000
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StringBuffer.cs
+++ /dev/null
@@ -1,301 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>
- /// Buffer that deals in char size increments. Dispose to free memory. Always makes ordinal
- /// comparisons. Not thread safe.
- ///
- /// A more performant replacement for StringBuilder when performing native interop.
- ///
- /// "No copy" valuetype. Has to be passed as "ref".
- ///
- /// </summary>
- /// <remarks>
- /// Suggested use through P/Invoke: define DllImport arguments that take a character buffer as SafeHandle and pass StringBuffer.GetHandle().
- /// </remarks>
- internal struct StringBuffer
- {
- private char[] _buffer;
- private int _length;
-
- /// <summary>
- /// Instantiate the buffer with capacity for at least the specified number of characters. Capacity
- /// includes the trailing null character.
- /// </summary>
- public StringBuffer(int initialCapacity)
- {
- _buffer = ArrayPool<char>.Shared.Rent(initialCapacity);
- _length = 0;
- }
-
- /// <summary>
- /// Get/set the character at the given index.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to index outside of the buffer length.</exception>
- public char this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
- return _buffer[index];
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
- _buffer[index] = value;
- }
- }
-
- /// <summary>
- /// Underlying storage of the buffer. Used for interop.
- /// </summary>
- public char[] UnderlyingArray => _buffer;
-
- /// <summary>
- /// Character capacity of the buffer. Includes the count for the trailing null character.
- /// </summary>
- public int Capacity => _buffer.Length;
-
- /// <summary>
- /// Ensure capacity in characters is at least the given minimum.
- /// </summary>
- /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
- public void EnsureCapacity(int minCapacity)
- {
- if (minCapacity > Capacity)
- {
- char[] oldBuffer = _buffer;
- _buffer = ArrayPool<char>.Shared.Rent(minCapacity);
- Array.Copy(oldBuffer, 0, _buffer, 0, oldBuffer.Length);
- ArrayPool<char>.Shared.Return(oldBuffer);
- }
- }
-
- /// <summary>
- /// The logical length of the buffer in characters. (Does not include the final null.) Will automatically attempt to increase capacity.
- /// This is where the usable data ends.
- /// </summary>
- /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if the set size in bytes is int.MaxValue (as space is implicitly reserved for the trailing null).</exception>
- public int Length
- {
- get { return _length; }
- set
- {
- // Null terminate
- EnsureCapacity(checked(value + 1));
- _buffer[value] = '\0';
-
- _length = value;
- }
- }
-
- /// <summary>
- /// True if the buffer contains the given character.
- /// </summary>
- public unsafe bool Contains(char value)
- {
- fixed (char* start = _buffer)
- {
- int length = _length;
- for (int i = 0; i < length; i++)
- {
- if (start[i] == value) return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Returns true if the buffer starts with the given string.
- /// </summary>
- public bool StartsWith(string value)
- {
- if (value == null) throw new ArgumentNullException(nameof(value));
- if (_length < value.Length) return false;
- return SubstringEquals(value, startIndex: 0, count: value.Length);
- }
-
- /// <summary>
- /// Returns true if the specified StringBuffer substring equals the given value.
- /// </summary>
- /// <param name="value">The value to compare against the specified substring.</param>
- /// <param name="startIndex">Start index of the sub string.</param>
- /// <param name="count">Length of the substring, or -1 to check all remaining.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
- /// of the buffer's length.
- /// </exception>
- public unsafe bool SubstringEquals(string value, int startIndex = 0, int count = -1)
- {
- if (value == null) return false;
- if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
- if (startIndex > _length) throw new ArgumentOutOfRangeException(nameof(startIndex));
-
- int realCount = count == -1 ? _length - startIndex : (int)count;
- if (checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
-
- int length = value.Length;
-
- // Check the substring length against the input length
- if (realCount != length) return false;
-
- fixed (char* valueStart = value)
- fixed (char* bufferStart = _buffer)
- {
- char* subStringStart = bufferStart + startIndex;
-
- for (int i = 0; i < length; i++)
- {
- if (subStringStart[i] != valueStart[i]) return false;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Append the given buffer.
- /// </summary>
- /// <param name="value">The buffer to append.</param>
- /// <param name="startIndex">The index in the input buffer to start appending from.</param>
- /// <param name="count">The count of characters to copy from the buffer string.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
- /// of <paramref name="value"/> characters.
- /// </exception>
- public void Append(ref StringBuffer value, int startIndex = 0)
- {
- if (value.Length == 0) return;
-
- value.CopyTo(
- bufferIndex: startIndex,
- destination: ref this,
- destinationIndex: _length,
- count: value.Length);
- }
-
- /// <summary>
- /// Append the given buffer.
- /// </summary>
- /// <param name="value">The buffer to append.</param>
- /// <param name="startIndex">The index in the input buffer to start appending from.</param>
- /// <param name="count">The count of characters to copy from the buffer string.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
- /// of <paramref name="value"/> characters.
- /// </exception>
- public void Append(ref StringBuffer value, int startIndex, int count)
- {
- if (count == 0) return;
-
- value.CopyTo(
- bufferIndex: startIndex,
- destination: ref this,
- destinationIndex: _length,
- count: count);
- }
-
- /// <summary>
- /// Copy contents to the specified buffer. Destination index must be within current destination length.
- /// Will grow the destination buffer if needed.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="bufferIndex"/> or <paramref name="destinationIndex"/> or <paramref name="count"/> are outside the range
- /// of <paramref name="value"/> characters.
- /// </exception>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="destination"/> is null.</exception>
- public void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count)
- {
- if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException(nameof(destinationIndex));
- if (bufferIndex >= _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
- if (_length < checked(bufferIndex + count)) throw new ArgumentOutOfRangeException(nameof(count));
-
- if (count == 0) return;
- int lastIndex = checked(destinationIndex + count);
- if (destination.Length < lastIndex) destination.Length = lastIndex;
-
- Array.Copy(UnderlyingArray, bufferIndex, destination.UnderlyingArray, destinationIndex, count);
- }
-
- /// <summary>
- /// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of
- /// the buffer, will grow as necessary.
- /// </summary>
- public void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (bufferIndex > _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
- if (sourceIndex < 0 || sourceIndex > source.Length) throw new ArgumentOutOfRangeException(nameof(sourceIndex));
- if (count == -1) count = source.Length - sourceIndex;
- if (count < 0 || source.Length - count < sourceIndex) throw new ArgumentOutOfRangeException(nameof(count));
-
- if (count == 0) return;
- int lastIndex = bufferIndex + (int)count;
- if (_length < lastIndex) Length = lastIndex;
-
- source.CopyTo(sourceIndex, UnderlyingArray, bufferIndex, count);
- }
-
- /// <summary>
- /// Trim the specified values from the end of the buffer. If nothing is specified, nothing is trimmed.
- /// </summary>
- public void TrimEnd(char[] values)
- {
- if (values == null || values.Length == 0 || _length == 0) return;
-
- while (_length > 0 && Array.IndexOf(values, _buffer[_length - 1]) >= 0)
- {
- Length = _length - 1;
- }
- }
-
- /// <summary>
- /// String representation of the entire buffer. If the buffer is larger than the maximum size string (int.MaxValue) this will throw.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the buffer is too big to fit into a string.</exception>
- public override string ToString()
- {
- return new string(_buffer, startIndex: 0, length: _length);
- }
-
- /// <summary>
- /// Get the given substring in the buffer.
- /// </summary>
- /// <param name="count">Count of characters to take, or remaining characters from <paramref name="startIndex"/> if -1.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range of the buffer's length
- /// or count is greater than the maximum string size (int.MaxValue).
- /// </exception>
- public string Substring(int startIndex, int count = -1)
- {
- if (startIndex > (_length == 0 ? 0 : _length - 1)) throw new ArgumentOutOfRangeException(nameof(startIndex));
- if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
-
- int realCount = count == -1 ? _length - startIndex : (int)count;
- if (realCount > int.MaxValue || checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
-
- // The buffer could be bigger than will fit into a string, but the substring might fit. As the starting
- // index might be bigger than int we need to index ourselves.
- return new string(_buffer, startIndex: startIndex, length: realCount);
- }
-
- public void Free()
- {
- ArrayPool<char>.Shared.Return(_buffer);
- _buffer = null;
- _length = 0;
- }
- }
-}
diff --git a/src/System.Private.CoreLib/shared/System/Security/SecureString.cs b/src/System.Private.CoreLib/shared/System/Security/SecureString.cs
index 9059f90e6..22f15acca 100644
--- a/src/System.Private.CoreLib/shared/System/Security/SecureString.cs
+++ b/src/System.Private.CoreLib/shared/System/Security/SecureString.cs
@@ -2,9 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Threading;
namespace System.Security
{
@@ -43,11 +43,8 @@ namespace System.Security
{
get
{
- lock (_methodLock)
- {
- EnsureNotDisposed();
- return _decryptedLength;
- }
+ EnsureNotDisposed();
+ return Volatile.Read(ref _decryptedLength);
}
}
@@ -108,20 +105,14 @@ namespace System.Security
public bool IsReadOnly()
{
- lock (_methodLock)
- {
- EnsureNotDisposed();
- return _readOnly;
- }
+ EnsureNotDisposed();
+ return Volatile.Read(ref _readOnly);
}
public void MakeReadOnly()
{
- lock (_methodLock)
- {
- EnsureNotDisposed();
- _readOnly = true;
- }
+ EnsureNotDisposed();
+ Volatile.Write(ref _readOnly, true);
}
public void RemoveAt(int index)
diff --git a/src/System.Private.CoreLib/shared/System/Single.cs b/src/System.Private.CoreLib/shared/System/Single.cs
index df97427d3..7bffa1ac7 100644
--- a/src/System.Private.CoreLib/shared/System/Single.cs
+++ b/src/System.Private.CoreLib/shared/System/Single.cs
@@ -16,6 +16,8 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
+using Internal.Runtime.CompilerServices;
+
namespace System
{
[Serializable]
@@ -213,16 +215,18 @@ namespace System
return IsNaN(obj) && IsNaN(m_value);
}
- public unsafe override int GetHashCode()
+ public override int GetHashCode()
{
- float f = m_value;
- if (f == 0)
+ var bits = Unsafe.As<float, int>(ref m_value);
+
+ // Optimized check for IsNan() || IsZero()
+ if (((bits - 1) & 0x7FFFFFFF) >= 0x7F800000)
{
- // Ensure that 0 and -0 have the same hash code
- return 0;
+ // Ensure that all NaNs and both zeros have the same hash code
+ bits &= 0x7F800000;
}
- int v = *(int*)(&f);
- return v;
+
+ return bits;
}
public override String ToString()
diff --git a/src/System.Private.CoreLib/shared/System/Span.Fast.cs b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
new file mode 100644
index 000000000..0ae1922fd
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
@@ -0,0 +1,350 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using Internal.Runtime.CompilerServices;
+
+#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
+
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ /// <summary>
+ /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
+ /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
+ /// </summary>
+ [DebuggerTypeProxy(typeof(SpanDebugView<>))]
+ [DebuggerDisplay("{ToString(),raw}")]
+ [NonVersionable]
+ public readonly ref partial struct Span<T>
+ {
+ /// <summary>A byref or a native ptr.</summary>
+ internal readonly ByReference<T> _pointer;
+ /// <summary>The number of elements this Span contains.</summary>
+#if PROJECTN
+ [Bound]
+#endif
+ private readonly int _length;
+
+ /// <summary>
+ /// Creates a new span over the entirety of the target array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span(T[] array)
+ {
+ if (array == null)
+ {
+ this = default;
+ return; // returns default
+ }
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+
+ _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
+ _length = array.Length;
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the span.</param>
+ /// <param name="length">The number of items in the span.</param>
+ /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span(T[] array, int start, int length)
+ {
+ if (array == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ this = default;
+ return; // returns default
+ }
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
+ _length = length;
+ }
+
+ /// <summary>
+ /// Creates a new span over the target unmanaged buffer. Clearly this
+ /// is quite dangerous, because we are creating arbitrarily typed T's
+ /// out of a void*-typed block of memory. And the length is not checked.
+ /// But if this creation is correct, then all subsequent uses are correct.
+ /// </summary>
+ /// <param name="pointer">An unmanaged pointer to memory.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
+ /// </exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="length"/> is negative.
+ /// </exception>
+ [CLSCompliant(false)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe Span(void* pointer, int length)
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ if (length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
+ _length = length;
+ }
+
+ // Constructor for internal use only.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Span(ref T ptr, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ _pointer = new ByReference<T>(ref ptr);
+ _length = length;
+ }
+
+ /// Returns a reference to specified element of the Span.
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ /// <exception cref="System.IndexOutOfRangeException">
+ /// Thrown when index less than 0 or index greater than or equal to Length
+ /// </exception>
+ public ref T this[int index]
+ {
+#if PROJECTN
+ [BoundsChecking]
+ get
+ {
+ return ref Unsafe.Add(ref _pointer.Value, index);
+ }
+#else
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [NonVersionable]
+ get
+ {
+ if ((uint)index >= (uint)_length)
+ ThrowHelper.ThrowIndexOutOfRangeException();
+ return ref Unsafe.Add(ref _pointer.Value, index);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Clears the contents of this span.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Clear()
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ SpanHelpers.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
+ }
+ else
+ {
+ SpanHelpers.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>());
+ }
+ }
+
+ /// <summary>
+ /// Fills the contents of this span with the given value.
+ /// </summary>
+ public void Fill(T value)
+ {
+ if (Unsafe.SizeOf<T>() == 1)
+ {
+ uint length = (uint)_length;
+ if (length == 0)
+ return;
+
+ T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below.
+ Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length);
+ }
+ else
+ {
+ // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations
+ nuint length = (uint)_length;
+ if (length == 0)
+ return;
+
+ ref T r = ref _pointer.Value;
+
+ // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16
+
+ nuint elementSize = (uint)Unsafe.SizeOf<T>();
+ nuint i = 0;
+ for (; i < (length & ~(nuint)7); i += 8)
+ {
+ Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value;
+ }
+ if (i < (length & ~(nuint)3))
+ {
+ Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
+ Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
+ i += 4;
+ }
+ for (; i < length; i++)
+ {
+ Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies the contents of this span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ /// </summary>
+ /// <param name="destination">The span to copy items into.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when the destination Span is shorter than the source Span.
+ /// </exception>
+ public void CopyTo(Span<T> destination)
+ {
+ // Using "if (!TryCopyTo(...))" results in two branches: one for the length
+ // check, and one for the result of TryCopyTo. Since these checks are equivalent,
+ // we can optimize by performing the check once ourselves then calling Memmove directly.
+
+ if ((uint)_length <= (uint)destination.Length)
+ {
+ Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
+ }
+ else
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+ }
+
+ /// <summary>
+ /// Copies the contents of this span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ /// </summary>
+ /// <param name="destination">The span to copy items into.</param>
+ /// <returns>If the destination span is shorter than the source span, this method
+ /// return false and no data is written to the destination.</returns>
+ public bool TryCopyTo(Span<T> destination)
+ {
+ bool retVal = false;
+ if ((uint)_length <= (uint)destination.Length)
+ {
+ Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /// <summary>
+ /// Returns true if left and right point at the same memory and have the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public static bool operator ==(Span<T> left, Span<T> right)
+ {
+ return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
+ }
+
+ /// <summary>
+ /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
+ /// </summary>
+ public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(ref span._pointer.Value, span._length);
+
+ /// <summary>
+ /// For <see cref="Span{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
+ /// Otherwise, returns a <see cref="String"/> with the name of the type and the number of elements.
+ /// </summary>
+ public override string ToString()
+ {
+ if (typeof(T) == typeof(char))
+ {
+ unsafe
+ {
+ fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
+ return new string(src, 0, _length);
+ }
+ }
+ return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given span, beginning at 'start'.
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(int start)
+ {
+ if ((uint)start > (uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given span, beginning at 'start', of given length
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(int start, int length)
+ {
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
+ }
+
+ /// <summary>
+ /// Copies the contents of this span into a new array. This heap
+ /// allocates, so should generally be avoided, however it is sometimes
+ /// necessary to bridge the gap with APIs written in terms of arrays.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] ToArray()
+ {
+ if (_length == 0)
+ return Array.Empty<T>();
+
+ var destination = new T[_length];
+ Buffer.Memmove(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length);
+ return destination;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Span.cs b/src/System.Private.CoreLib/shared/System/Span.cs
index 5a813174d..78733515c 100644
--- a/src/System.Private.CoreLib/shared/System/Span.cs
+++ b/src/System.Private.CoreLib/shared/System/Span.cs
@@ -5,17 +5,12 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+#if !FEATURE_PORTABLE_SPAN
using System.Runtime.Versioning;
-using Internal.Runtime.CompilerServices;
+#endif // !FEATURE_PORTABLE_SPAN
#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
-#if BIT64
-using nuint = System.UInt64;
-#else
-using nuint = System.UInt32;
-#endif
-
namespace System
{
/// <summary>
@@ -23,134 +18,17 @@ namespace System
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
[DebuggerTypeProxy(typeof(SpanDebugView<>))]
- [DebuggerDisplay("{DebuggerDisplay,nq}")]
- [NonVersionable]
- public readonly ref struct Span<T>
+ [DebuggerDisplay("{ToString(),raw}")]
+ public readonly ref partial struct Span<T>
{
- /// <summary>A byref or a native ptr.</summary>
- internal readonly ByReference<T> _pointer;
- /// <summary>The number of elements this Span contains.</summary>
-#if PROJECTN
- [Bound]
-#endif
- private readonly int _length;
-
- /// <summary>
- /// Creates a new span over the entirety of the target array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
- _length = array.Length;
- }
-
- /// <summary>
- /// Creates a new span over the portion of the target array beginning
- /// at 'start' index and ending at 'end' index (exclusive).
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the span.</param>
- /// <param name="length">The number of items in the span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array, int start, int length)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
- if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
- _length = length;
- }
-
- /// <summary>
- /// Creates a new span over the target unmanaged buffer. Clearly this
- /// is quite dangerous, because we are creating arbitrarily typed T's
- /// out of a void*-typed block of memory. And the length is not checked.
- /// But if this creation is correct, then all subsequent uses are correct.
- /// </summary>
- /// <param name="pointer">An unmanaged pointer to memory.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
- /// </exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="length"/> is negative.
- /// </exception>
- [CLSCompliant(false)]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe Span(void* pointer, int length)
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
- if (length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
- _length = length;
- }
-
- /// <summary>
- /// Create a new span over a portion of a regular managed object. This can be useful
- /// if part of a managed object represents a "fixed array." This is dangerous because neither the
- /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
- /// "rawPointer" actually lies within <paramref name="obj"/>.
- /// </summary>
- /// <param name="obj">The managed object that contains the data to span over.</param>
- /// <param name="objectData">A reference to data within that object.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length);
-
- // Constructor for internal use only.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Span(ref T ptr, int length)
- {
- Debug.Assert(length >= 0);
-
- _pointer = new ByReference<T>(ref ptr);
- _length = length;
- }
-
- //Debugger Display = {T[length]}
- private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length);
-
- /// <summary>
- /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
- /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal ref T DangerousGetPinnableReference()
- {
- return ref _pointer.Value;
- }
-
/// <summary>
/// The number of items in the span.
/// </summary>
public int Length
{
+#if !FEATURE_PORTABLE_SPAN
[NonVersionable]
+#endif // !FEATURE_PORTABLE_SPAN
get
{
return _length;
@@ -162,7 +40,9 @@ namespace System
/// </summary>
public bool IsEmpty
{
+#if !FEATURE_PORTABLE_SPAN
[NonVersionable]
+#endif // !FEATURE_PORTABLE_SPAN
get
{
return _length == 0;
@@ -170,145 +50,6 @@ namespace System
}
/// <summary>
- /// Returns a reference to specified element of the Span.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- /// <exception cref="System.IndexOutOfRangeException">
- /// Thrown when index less than 0 or index greater than or equal to Length
- /// </exception>
- public ref T this[int index]
- {
-#if PROJECTN
- [BoundsChecking]
- get
- {
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
-#else
- [Intrinsic]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [NonVersionable]
- get
- {
- if ((uint)index >= (uint)_length)
- ThrowHelper.ThrowIndexOutOfRangeException();
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
-#endif
- }
-
- /// <summary>
- /// Clears the contents of this span.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear()
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- {
- Span.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
- }
- else
- {
- Span.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>());
- }
- }
-
- /// <summary>
- /// Fills the contents of this span with the given value.
- /// </summary>
- public void Fill(T value)
- {
- if (Unsafe.SizeOf<T>() == 1)
- {
- uint length = (uint)_length;
- if (length == 0)
- return;
-
- T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below.
- Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length);
- }
- else
- {
- // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations
- nuint length = (uint)_length;
- if (length == 0)
- return;
-
- ref T r = ref DangerousGetPinnableReference();
-
- // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16
-
- nuint elementSize = (uint)Unsafe.SizeOf<T>();
- nuint i = 0;
- for (; i < (length & ~(nuint)7); i += 8)
- {
- Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value;
- }
- if (i < (length & ~(nuint)3))
- {
- Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
- i += 4;
- }
- for (; i < length; i++)
- {
- Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value;
- }
- }
- }
-
- /// <summary>
- /// Copies the contents of this span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <param name="destination">The span to copy items into.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when the destination Span is shorter than the source Span.
- /// </exception>
- public void CopyTo(Span<T> destination)
- {
- if (!TryCopyTo(destination))
- ThrowHelper.ThrowArgumentException_DestinationTooShort();
- }
-
- /// <summary>
- /// Copies the contents of this span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <param name="destination">The span to copy items into.</param>
- /// <returns>If the destination span is shorter than the source span, this method
- /// return false and no data is written to the destination.</returns>
- public bool TryCopyTo(Span<T> destination)
- {
- if ((uint)_length > (uint)destination.Length)
- return false;
-
- Span.CopyTo<T>(ref destination._pointer.Value, ref _pointer.Value, _length);
- return true;
- }
-
- /// <summary>
- /// Returns true if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator ==(Span<T> left, Span<T> right)
- {
- return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
- }
-
- /// <summary>
/// Returns false if left and right point at the same memory and have the same length. Note that
/// this does *not* check to see if the *contents* are equal.
/// </summary>
@@ -343,69 +84,15 @@ namespace System
/// <summary>
/// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
/// </summary>
- public static implicit operator Span<T>(T[] array) => array != null ? new Span<T>(array) : default;
+ public static implicit operator Span<T>(T[] array) => new Span<T>(array);
/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(ArraySegment<T> arraySegment)
- => arraySegment.Array != null ? new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default;
-
- /// <summary>
- /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
- /// </summary>
- public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(ref span._pointer.Value, span._length);
-
- /// <summary>
- /// Forms a slice out of the given span, beginning at 'start'.
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span<T> Slice(int start)
- {
- if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
- }
+ => new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
/// <summary>
- /// Forms a slice out of the given span, beginning at 'start', of given length
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The desired length for the slice (exclusive).</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span<T> Slice(int start, int length)
- {
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
- }
-
- /// <summary>
- /// Copies the contents of this span into a new array. This heap
- /// allocates, so should generally be avoided, however it is sometimes
- /// necessary to bridge the gap with APIs written in terms of arrays.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public T[] ToArray()
- {
- if (_length == 0)
- return Array.Empty<T>();
-
- var destination = new T[_length];
- Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length);
- return destination;
- }
-
- // <summary>
/// Returns an empty <see cref="Span{T}"/>
/// </summary>
public static Span<T> Empty => default(Span<T>);
diff --git a/src/System.Private.CoreLib/shared/System/SpanHelpers.BinarySearch.cs b/src/System.Private.CoreLib/shared/System/SpanHelpers.BinarySearch.cs
new file mode 100644
index 000000000..656b864e2
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/SpanHelpers.BinarySearch.cs
@@ -0,0 +1,83 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
+
+namespace System
+{
+ internal static partial class SpanHelpers
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int BinarySearch<T, TComparable>(
+ this ReadOnlySpan<T> span, TComparable comparable)
+ where TComparable : IComparable<T>
+ {
+ if (comparable == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparable);
+
+ return BinarySearch(ref MemoryMarshal.GetReference(span), span.Length, comparable);
+ }
+
+ public static int BinarySearch<T, TComparable>(
+ ref T spanStart, int length, TComparable comparable)
+ where TComparable : IComparable<T>
+ {
+ int lo = 0;
+ int hi = length - 1;
+ // If length == 0, hi == -1, and loop will not be entered
+ while (lo <= hi)
+ {
+ // PERF: `lo` or `hi` will never be negative inside the loop,
+ // so computing median using uints is safe since we know
+ // `length <= int.MaxValue`, and indices are >= 0
+ // and thus cannot overflow an uint.
+ // Saves one subtraction per loop compared to
+ // `int i = lo + ((hi - lo) >> 1);`
+ int i = (int)(((uint)hi + (uint)lo) >> 1);
+
+ int c = comparable.CompareTo(Unsafe.Add(ref spanStart, i));
+ if (c == 0)
+ {
+ return i;
+ }
+ else if (c > 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i - 1;
+ }
+ }
+ // If none found, then a negative number that is the bitwise complement
+ // of the index of the next element that is larger than or, if there is
+ // no larger element, the bitwise complement of `length`, which
+ // is `lo` at this point.
+ return ~lo;
+ }
+
+ // Helper to allow sharing all code via IComparable<T> inlineable
+ internal struct ComparerComparable<T, TComparer> : IComparable<T>
+ where TComparer : IComparer<T>
+ {
+ readonly T _value;
+ readonly TComparer _comparer;
+
+ public ComparerComparable(T value, TComparer comparer)
+ {
+ _value = value;
+ _comparer = comparer;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int CompareTo(T other) => _comparer.Compare(_value, other);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/SpanHelpers.Byte.cs b/src/System.Private.CoreLib/shared/System/SpanHelpers.Byte.cs
new file mode 100644
index 000000000..860b2efa0
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/SpanHelpers.Byte.cs
@@ -0,0 +1,1104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
+
+#if !netstandard11
+using System.Numerics;
+#endif
+
+namespace System
+{
+ internal static partial class SpanHelpers
+ {
+ public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ byte valueHead = value;
+ ref byte valueTail = ref Unsafe.Add(ref value, 1);
+ int valueTailLength = valueLength - 1;
+
+ int index = 0;
+ for (; ; )
+ {
+ Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
+ int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
+ if (remainingSearchSpaceLength <= 0)
+ break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
+
+ // Do a quick search for the first element of "value".
+ int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
+ if (relativeIndex == -1)
+ break;
+ index += relativeIndex;
+
+ // Found the first element of "value". See if the tail matches.
+ if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
+ return index; // The tail matched. Return a successful find.
+
+ index++;
+ }
+ return -1;
+ }
+
+ public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ int index = -1;
+ for (int i = 0; i < valueLength; i++)
+ {
+ var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
+ if ((uint)tempIndex < (uint)index)
+ {
+ index = tempIndex;
+ // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value
+ searchSpaceLength = tempIndex;
+
+ if (index == 0) break;
+ }
+ }
+ return index;
+ }
+
+ public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ int index = -1;
+ for (int i = 0; i < valueLength; i++)
+ {
+ var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
+ if (tempIndex > index) index = tempIndex;
+ }
+ return index;
+ }
+
+ public static unsafe int IndexOf(ref byte searchSpace, byte value, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(uint)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 1))
+ goto Found1;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 2))
+ goto Found2;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 3))
+ goto Found3;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 4))
+ goto Found4;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 5))
+ goto Found5;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 6))
+ goto Found6;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 7))
+ goto Found7;
+
+ index += 8;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 1))
+ goto Found1;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 2))
+ goto Found2;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 3))
+ goto Found3;
+
+ index += 4;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+
+ index += 1;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
+ {
+ nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector<byte>.Count - 1));
+ // Get comparison Vector
+ Vector<byte> vComparison = GetVector(value);
+ while ((byte*)nLength > (byte*)index)
+ {
+ var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index)));
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index += Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)index + LocateFirstFoundByte(vMatches);
+ }
+
+ if ((int)(byte*)index < length)
+ {
+ unchecked
+ {
+ nLength = (IntPtr)(length - (int)(byte*)index);
+ }
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static int LastIndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ byte valueHead = value;
+ ref byte valueTail = ref Unsafe.Add(ref value, 1);
+ int valueTailLength = valueLength - 1;
+
+ int index = 0;
+ for (; ; )
+ {
+ Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
+ int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
+ if (remainingSearchSpaceLength <= 0)
+ break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
+
+ // Do a quick search for the first element of "value".
+ int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
+ if (relativeIndex == -1)
+ break;
+
+ // Found the first element of "value". See if the tail matches.
+ if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
+ return relativeIndex; // The tail matched. Return a successful find.
+
+ index += remainingSearchSpaceLength - relativeIndex;
+ }
+ return -1;
+ }
+
+ public static unsafe int LastIndexOf(ref byte searchSpace, byte value, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+ index -= 8;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index + 7))
+ goto Found7;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 6))
+ goto Found6;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 5))
+ goto Found5;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 4))
+ goto Found4;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 3))
+ goto Found3;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 2))
+ goto Found2;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 1))
+ goto Found1;
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+ index -= 4;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index + 3))
+ goto Found3;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 2))
+ goto Found2;
+ if (uValue == Unsafe.Add(ref searchSpace, index + 1))
+ goto Found1;
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+ index -= 1;
+
+ if (uValue == Unsafe.Add(ref searchSpace, index))
+ goto Found;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0))
+ {
+ nLength = (IntPtr)(uint)((uint)index & ~(Vector<byte>.Count - 1));
+
+ // Get comparison Vector
+ Vector<byte> vComparison = GetVector(value);
+ while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
+ {
+ var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count)));
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index -= Vector<byte>.Count;
+ nLength -= Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
+ }
+ if ((int)(byte*)index > 0)
+ {
+ nLength = index;
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(uint)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ uint lookUp;
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found7;
+
+ index += 8;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found3;
+
+ index += 4;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+
+ index += 1;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
+ {
+ nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector<byte>.Count - 1));
+ // Get comparison Vector
+ Vector<byte> values0 = GetVector(value0);
+ Vector<byte> values1 = GetVector(value1);
+
+ while ((byte*)nLength > (byte*)index)
+ {
+ Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index));
+ var vMatches = Vector.BitwiseOr(
+ Vector.Equals(vData, values0),
+ Vector.Equals(vData, values1));
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index += Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)index + LocateFirstFoundByte(vMatches);
+ }
+
+ if ((int)(byte*)index < length)
+ {
+ unchecked
+ {
+ nLength = (IntPtr)(length - (int)(byte*)index);
+ }
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(uint)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ uint lookUp;
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found7;
+
+ index += 8;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found3;
+
+ index += 4;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+
+ index += 1;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
+ {
+ nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector<byte>.Count - 1));
+ // Get comparison Vector
+ Vector<byte> values0 = GetVector(value0);
+ Vector<byte> values1 = GetVector(value1);
+ Vector<byte> values2 = GetVector(value2);
+ while ((byte*)nLength > (byte*)index)
+ {
+ Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index));
+
+ var vMatches = Vector.BitwiseOr(
+ Vector.BitwiseOr(
+ Vector.Equals(vData, values0),
+ Vector.Equals(vData, values1)),
+ Vector.Equals(vData, values2));
+
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index += Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)index + LocateFirstFoundByte(vMatches);
+ }
+
+ if ((int)(byte*)index < length)
+ {
+ unchecked
+ {
+ nLength = (IntPtr)(length - (int)(byte*)index);
+ }
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ uint lookUp;
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+ index -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found7;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+ index -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+ index -= 1;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp)
+ goto Found;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0))
+ {
+ nLength = (IntPtr)(uint)((uint)index & ~(Vector<byte>.Count - 1));
+ // Get comparison Vector
+ Vector<byte> values0 = GetVector(value0);
+ Vector<byte> values1 = GetVector(value1);
+
+ while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
+ {
+ Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count));
+ var vMatches = Vector.BitwiseOr(
+ Vector.Equals(vData, values0),
+ Vector.Equals(vData, values1));
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index -= Vector<byte>.Count;
+ nLength -= Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
+ }
+
+ if ((int)(byte*)index > 0)
+ {
+ nLength = index;
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+ IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr nLength = (IntPtr)(uint)length;
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+ {
+ unchecked
+ {
+ int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+ nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
+ }
+ }
+ SequentialScan:
+#endif
+ uint lookUp;
+ while ((byte*)nLength >= (byte*)8)
+ {
+ nLength -= 8;
+ index -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found7;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+ }
+
+ if ((byte*)nLength >= (byte*)4)
+ {
+ nLength -= 4;
+ index -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+ }
+
+ while ((byte*)nLength > (byte*)0)
+ {
+ nLength -= 1;
+ index -= 1;
+
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
+ goto Found;
+ }
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0))
+ {
+ nLength = (IntPtr)(uint)((uint)index & ~(Vector<byte>.Count - 1));
+ // Get comparison Vector
+ Vector<byte> values0 = GetVector(value0);
+ Vector<byte> values1 = GetVector(value1);
+ Vector<byte> values2 = GetVector(value2);
+ while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
+ {
+ Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count));
+
+ var vMatches = Vector.BitwiseOr(
+ Vector.BitwiseOr(
+ Vector.Equals(vData, values0),
+ Vector.Equals(vData, values1)),
+ Vector.Equals(vData, values2));
+
+ if (Vector<byte>.Zero.Equals(vMatches))
+ {
+ index -= Vector<byte>.Count;
+ nLength -= Vector<byte>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(byte*)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
+ }
+
+ if ((int)(byte*)index > 0)
+ {
+ nLength = index;
+ goto SequentialScan;
+ }
+ }
+#endif
+ return -1;
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static unsafe bool SequenceEqual(ref byte first, ref byte second, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ if (Unsafe.AreSame(ref first, ref second))
+ goto Equal;
+
+ IntPtr i = (IntPtr)0; // Use IntPtr and byte* for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr n = (IntPtr)length;
+
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && (byte*)n >= (byte*)Vector<byte>.Count)
+ {
+ n -= Vector<byte>.Count;
+ while ((byte*)n > (byte*)i)
+ {
+ if (Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, i)) !=
+ Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, i)))
+ {
+ goto NotEqual;
+ }
+ i += Vector<byte>.Count;
+ }
+ return Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, n)) ==
+ Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, n));
+ }
+#endif
+
+ if ((byte*)n >= (byte*)sizeof(UIntPtr))
+ {
+ n -= sizeof(UIntPtr);
+ while ((byte*)n > (byte*)i)
+ {
+ if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, i)) !=
+ Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, i)))
+ {
+ goto NotEqual;
+ }
+ i += sizeof(UIntPtr);
+ }
+ return Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, n)) ==
+ Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, n));
+ }
+
+ while ((byte*)n > (byte*)i)
+ {
+ if (Unsafe.AddByteOffset(ref first, i) != Unsafe.AddByteOffset(ref second, i))
+ goto NotEqual;
+ i += 1;
+ }
+
+ Equal:
+ return true;
+
+ NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return false;
+ }
+
+#if !netstandard11
+ // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateFirstFoundByte(Vector<byte> match)
+ {
+ var vector64 = Vector.AsVectorUInt64(match);
+ ulong candidate = 0;
+ int i = 0;
+ // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
+ for (; i < Vector<ulong>.Count; i++)
+ {
+ candidate = vector64[i];
+ if (candidate != 0)
+ {
+ break;
+ }
+ }
+
+ // Single LEA instruction with jitted const (using function result)
+ return i * 8 + LocateFirstFoundByte(candidate);
+ }
+#endif
+
+ public static unsafe int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength)
+ {
+ Debug.Assert(firstLength >= 0);
+ Debug.Assert(secondLength >= 0);
+
+ if (Unsafe.AreSame(ref first, ref second))
+ goto Equal;
+
+ var minLength = firstLength;
+ if (minLength > secondLength) minLength = secondLength;
+
+ IntPtr i = (IntPtr)0; // Use IntPtr and byte* for arithmetic to avoid unnecessary 64->32->64 truncations
+ IntPtr n = (IntPtr)minLength;
+
+#if !netstandard11
+ if (Vector.IsHardwareAccelerated && (byte*)n > (byte*)Vector<byte>.Count)
+ {
+ n -= Vector<byte>.Count;
+ while ((byte*)n > (byte*)i)
+ {
+ if (Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, i)) !=
+ Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, i)))
+ {
+ goto NotEqual;
+ }
+ i += Vector<byte>.Count;
+ }
+ goto NotEqual;
+ }
+#endif
+
+ if ((byte*)n > (byte*)sizeof(UIntPtr))
+ {
+ n -= sizeof(UIntPtr);
+ while ((byte*)n > (byte*)i)
+ {
+ if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, i)) !=
+ Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, i)))
+ {
+ goto NotEqual;
+ }
+ i += sizeof(UIntPtr);
+ }
+ }
+
+ NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ while((byte*)minLength > (byte*)i)
+ {
+ int result = Unsafe.AddByteOffset(ref first, i).CompareTo(Unsafe.AddByteOffset(ref second, i));
+ if (result != 0) return result;
+ i += 1;
+ }
+
+ Equal:
+ return firstLength - secondLength;
+ }
+
+#if !netstandard11
+ // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateLastFoundByte(Vector<byte> match)
+ {
+ var vector64 = Vector.AsVectorUInt64(match);
+ ulong candidate = 0;
+ int i = Vector<ulong>.Count - 1;
+ // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
+ for (; i >= 0; i--)
+ {
+ candidate = vector64[i];
+ if (candidate != 0)
+ {
+ break;
+ }
+ }
+
+ // Single LEA instruction with jitted const (using function result)
+ return i * 8 + LocateLastFoundByte(candidate);
+ }
+#endif
+
+#if !netstandard11
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateFirstFoundByte(ulong match)
+ {
+ unchecked
+ {
+ // Flag least significant power of two bit
+ var powerOfTwoFlag = match ^ (match - 1);
+ // Shift all powers of two into the high byte and extract
+ return (int)((powerOfTwoFlag * XorPowerOfTwoToHighByte) >> 57);
+ }
+ }
+#endif
+
+#if !netstandard11
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateLastFoundByte(ulong match)
+ {
+ // Find the most significant byte that has its highest bit set
+ int index = 7;
+ while ((long)match > 0)
+ {
+ match = match << 8;
+ index--;
+ }
+ return index;
+ }
+#endif
+
+#if !netstandard11
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector<byte> GetVector(byte vectorByte)
+ {
+#if !netcoreapp
+ // Vector<byte> .ctor doesn't become an intrinsic due to detection issue
+ // However this does cause it to become an intrinsic (with additional multiply and reg->reg copy)
+ // https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670
+ return Vector.AsVectorByte(new Vector<uint>(vectorByte * 0x01010101u));
+#else
+ return new Vector<byte>(vectorByte);
+#endif
+ }
+#endif
+
+#if !netstandard11
+ private const ulong XorPowerOfTwoToHighByte = (0x07ul |
+ 0x06ul << 8 |
+ 0x05ul << 16 |
+ 0x04ul << 24 |
+ 0x03ul << 32 |
+ 0x02ul << 40 |
+ 0x01ul << 48) + 1;
+#endif
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/SpanHelpers.T.cs b/src/System.Private.CoreLib/shared/System/SpanHelpers.T.cs
new file mode 100644
index 000000000..d1c62c8d5
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/SpanHelpers.T.cs
@@ -0,0 +1,683 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#else
+using System.Runtime.CompilerServices;
+#endif
+
+namespace System
+{
+ internal static partial class SpanHelpers
+ {
+ public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ T valueHead = value;
+ ref T valueTail = ref Unsafe.Add(ref value, 1);
+ int valueTailLength = valueLength - 1;
+
+ int index = 0;
+ for (; ; )
+ {
+ Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
+ int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
+ if (remainingSearchSpaceLength <= 0)
+ break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
+
+ // Do a quick search for the first element of "value".
+ int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
+ if (relativeIndex == -1)
+ break;
+ index += relativeIndex;
+
+ // Found the first element of "value". See if the tail matches.
+ if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
+ return index; // The tail matched. Return a successful find.
+
+ index++;
+ }
+ return -1;
+ }
+
+ public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ while (length >= 8)
+ {
+ length -= 8;
+
+ if (value.Equals(Unsafe.Add(ref searchSpace, index)))
+ goto Found;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
+ goto Found1;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
+ goto Found2;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
+ goto Found3;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 4)))
+ goto Found4;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 5)))
+ goto Found5;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 6)))
+ goto Found6;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
+ goto Found7;
+
+ index += 8;
+ }
+
+ if (length >= 4)
+ {
+ length -= 4;
+
+ if (value.Equals(Unsafe.Add(ref searchSpace, index)))
+ goto Found;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
+ goto Found1;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
+ goto Found2;
+ if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
+ goto Found3;
+
+ index += 4;
+ }
+
+ while (length > 0)
+ {
+ if (value.Equals(Unsafe.Add(ref searchSpace, index)))
+ goto Found;
+
+ index += 1;
+ length--;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return (int)(byte*)index;
+ Found1:
+ return (int)(byte*)(index + 1);
+ Found2:
+ return (int)(byte*)(index + 2);
+ Found3:
+ return (int)(byte*)(index + 3);
+ Found4:
+ return (int)(byte*)(index + 4);
+ Found5:
+ return (int)(byte*)(index + 5);
+ Found6:
+ return (int)(byte*)(index + 6);
+ Found7:
+ return (int)(byte*)(index + 7);
+ }
+
+ public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ T lookUp;
+ int index = 0;
+ while ((length - index) >= 8)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found7;
+
+ index += 8;
+ }
+
+ if ((length - index) >= 4)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found3;
+
+ index += 4;
+ }
+
+ while (index < length)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+
+ index++;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return index;
+ Found1:
+ return index + 1;
+ Found2:
+ return index + 2;
+ Found3:
+ return index + 3;
+ Found4:
+ return index + 4;
+ Found5:
+ return index + 5;
+ Found6:
+ return index + 6;
+ Found7:
+ return index + 7;
+ }
+
+ public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ T lookUp;
+ int index = 0;
+ while ((length - index) >= 8)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, index + 4);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, index + 5);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, index + 6);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, index + 7);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found7;
+
+ index += 8;
+ }
+
+ if ((length - index) >= 4)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+ lookUp = Unsafe.Add(ref searchSpace, index + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, index + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, index + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found3;
+
+ index += 4;
+ }
+
+ while (index < length)
+ {
+ lookUp = Unsafe.Add(ref searchSpace, index);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+
+ index++;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return index;
+ Found1:
+ return index + 1;
+ Found2:
+ return index + 2;
+ Found3:
+ return index + 3;
+ Found4:
+ return index + 4;
+ Found5:
+ return index + 5;
+ Found6:
+ return index + 6;
+ Found7:
+ return index + 7;
+ }
+
+ public static int IndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ int index = -1;
+ for (int i = 0; i < valueLength; i++)
+ {
+ var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
+ if ((uint)tempIndex < (uint)index)
+ {
+ index = tempIndex;
+ // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value
+ searchSpaceLength = tempIndex;
+
+ if (index == 0) break;
+ }
+ }
+ return index;
+ }
+
+ public static int LastIndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ T valueHead = value;
+ ref T valueTail = ref Unsafe.Add(ref value, 1);
+ int valueTailLength = valueLength - 1;
+
+ int index = 0;
+ for (; ; )
+ {
+ Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
+ int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
+ if (remainingSearchSpaceLength <= 0)
+ break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
+
+ // Do a quick search for the first element of "value".
+ int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
+ if (relativeIndex == -1)
+ break;
+
+ // Found the first element of "value". See if the tail matches.
+ if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
+ return relativeIndex; // The tail matched. Return a successful find.
+
+ index += remainingSearchSpaceLength - relativeIndex;
+ }
+ return -1;
+ }
+
+ public static int LastIndexOf<T>(ref T searchSpace, T value, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ while (length >= 8)
+ {
+ length -= 8;
+
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 7)))
+ goto Found7;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 6)))
+ goto Found6;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 5)))
+ goto Found5;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 4)))
+ goto Found4;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
+ goto Found3;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
+ goto Found2;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
+ goto Found1;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length)))
+ goto Found;
+ }
+
+ if (length >= 4)
+ {
+ length -= 4;
+
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
+ goto Found3;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
+ goto Found2;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
+ goto Found1;
+ if (value.Equals(Unsafe.Add(ref searchSpace, length)))
+ goto Found;
+ }
+
+ while (length > 0)
+ {
+ length--;
+
+ if (value.Equals(Unsafe.Add(ref searchSpace, length)))
+ goto Found;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return length;
+ Found1:
+ return length + 1;
+ Found2:
+ return length + 2;
+ Found3:
+ return length + 3;
+ Found4:
+ return length + 4;
+ Found5:
+ return length + 5;
+ Found6:
+ return length + 6;
+ Found7:
+ return length + 7;
+ }
+
+ public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ T lookUp;
+ while (length >= 8)
+ {
+ length -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, length + 7);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found7;
+ lookUp = Unsafe.Add(ref searchSpace, length + 6);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, length + 5);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, length + 4);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, length + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, length + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, length + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+ }
+
+ if (length >= 4)
+ {
+ length -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, length + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, length + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, length + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+ }
+
+ while (length > 0)
+ {
+ length--;
+
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp))
+ goto Found;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return length;
+ Found1:
+ return length + 1;
+ Found2:
+ return length + 2;
+ Found3:
+ return length + 3;
+ Found4:
+ return length + 4;
+ Found5:
+ return length + 5;
+ Found6:
+ return length + 6;
+ Found7:
+ return length + 7;
+ }
+
+ public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ T lookUp;
+ while (length >= 8)
+ {
+ length -= 8;
+
+ lookUp = Unsafe.Add(ref searchSpace, length + 7);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found7;
+ lookUp = Unsafe.Add(ref searchSpace, length + 6);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found6;
+ lookUp = Unsafe.Add(ref searchSpace, length + 5);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found5;
+ lookUp = Unsafe.Add(ref searchSpace, length + 4);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found4;
+ lookUp = Unsafe.Add(ref searchSpace, length + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, length + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, length + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+ }
+
+ if (length >= 4)
+ {
+ length -= 4;
+
+ lookUp = Unsafe.Add(ref searchSpace, length + 3);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found3;
+ lookUp = Unsafe.Add(ref searchSpace, length + 2);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found2;
+ lookUp = Unsafe.Add(ref searchSpace, length + 1);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found1;
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+ }
+
+ while (length > 0)
+ {
+ length--;
+
+ lookUp = Unsafe.Add(ref searchSpace, length);
+ if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
+ goto Found;
+ }
+ return -1;
+
+ Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return length;
+ Found1:
+ return length + 1;
+ Found2:
+ return length + 2;
+ Found3:
+ return length + 3;
+ Found4:
+ return length + 4;
+ Found5:
+ return length + 5;
+ Found6:
+ return length + 6;
+ Found7:
+ return length + 7;
+ }
+
+ public static int LastIndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(searchSpaceLength >= 0);
+ Debug.Assert(valueLength >= 0);
+
+ if (valueLength == 0)
+ return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
+
+ int index = -1;
+ for (int i = 0; i < valueLength; i++)
+ {
+ var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
+ if (tempIndex > index) index = tempIndex;
+ }
+ return index;
+ }
+
+ public static bool SequenceEqual<T>(ref T first, ref T second, int length)
+ where T : IEquatable<T>
+ {
+ Debug.Assert(length >= 0);
+
+ if (Unsafe.AreSame(ref first, ref second))
+ goto Equal;
+
+ IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+ while (length >= 8)
+ {
+ length -= 8;
+
+ if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 4).Equals(Unsafe.Add(ref second, index + 4)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 5).Equals(Unsafe.Add(ref second, index + 5)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 6).Equals(Unsafe.Add(ref second, index + 6)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 7).Equals(Unsafe.Add(ref second, index + 7)))
+ goto NotEqual;
+
+ index += 8;
+ }
+
+ if (length >= 4)
+ {
+ length -= 4;
+
+ if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2)))
+ goto NotEqual;
+ if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3)))
+ goto NotEqual;
+
+ index += 4;
+ }
+
+ while (length > 0)
+ {
+ if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index)))
+ goto NotEqual;
+ index += 1;
+ length--;
+ }
+
+ Equal:
+ return true;
+
+ NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
+ return false;
+ }
+
+ public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T second, int secondLength)
+ where T : IComparable<T>
+ {
+ Debug.Assert(firstLength >= 0);
+ Debug.Assert(secondLength >= 0);
+
+ var minLength = firstLength;
+ if (minLength > secondLength) minLength = secondLength;
+ for (int i = 0; i < minLength; i++)
+ {
+ int result = Unsafe.Add(ref first, i).CompareTo(Unsafe.Add(ref second, i));
+ if (result != 0) return result;
+ }
+ return firstLength.CompareTo(secondLength);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs b/src/System.Private.CoreLib/shared/System/SpanHelpers.cs
index 775426f6b..dad0f6294 100644
--- a/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs
+++ b/src/System.Private.CoreLib/shared/System/SpanHelpers.cs
@@ -3,8 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Globalization;
using System.Runtime;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;
@@ -17,196 +17,137 @@ using nuint = System.UInt32;
namespace System
{
- /// <summary>
- /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory.
- /// </summary>
- public static class Span
+ internal static partial class SpanHelpers
{
- /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
- /// <param name="text">The target string.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null reference (Nothing in Visual Basic).</exception>
- public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text)
+ public static int IndexOfCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (text == null)
+ Debug.Assert(span.Length != 0);
+ Debug.Assert(value.Length != 0);
+
+ if (GlobalizationMode.Invariant)
{
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+ return CompareInfo.InvariantIndexOf(span, value, ignoreCase: false);
}
- return new ReadOnlyMemory<char>(text, 0, text.Length);
+ return compareInfo.IndexOf(span, value, CompareOptions.None);
}
- /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
- /// <param name="readOnlyMemory">The memory that may be wrapping a <see cref="string"/> object.</param>
- /// <param name="text">The string.</param>
- /// <param name="start">The starting location in <paramref name="text"/>.</param>
- /// <param name="length">The number of items in <paramref name="text"/>.</param>
- /// <returns></returns>
- public static bool TryGetString(this ReadOnlyMemory<char> readOnlyMemory, out string text, out int start, out int length)
+ public static int IndexOfCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s)
- {
- text = s;
- start = offset;
- length = count;
- return true;
- }
- else
+ Debug.Assert(span.Length != 0);
+ Debug.Assert(value.Length != 0);
+
+ if (GlobalizationMode.Invariant)
{
- text = null;
- start = 0;
- length = 0;
- return false;
+ return CompareInfo.InvariantIndexOf(span, value, ignoreCase: true);
}
+
+ return compareInfo.IndexOf(span, value, CompareOptions.IgnoreCase);
}
- /// <summary>
- /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
- /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<byte> AsBytes<T>(this Span<T> source)
- where T : struct
+ public static int IndexOfOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, bool ignoreCase)
{
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ Debug.Assert(span.Length != 0);
+ Debug.Assert(value.Length != 0);
- return new Span<byte>(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
- checked(source.Length * Unsafe.SizeOf<T>()));
+ if (GlobalizationMode.Invariant)
+ {
+ return CompareInfo.InvariantIndexOf(span, value, ignoreCase);
+ }
+
+ return CompareInfo.Invariant.IndexOfOrdinal(span, value, ignoreCase);
}
- /// <summary>
- /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
- /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
- where T : struct
+ public static bool StartsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ Debug.Assert(value.Length != 0);
- return new ReadOnlySpan<byte>(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
- checked(source.Length * Unsafe.SizeOf<T>()));
+ if (GlobalizationMode.Invariant)
+ {
+ return span.StartsWith(value);
+ }
+ if (span.Length == 0)
+ {
+ return false;
+ }
+ return compareInfo.IsPrefix(span, value, CompareOptions.None);
}
- /// <summary>
- /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <remarks>
- /// Supported only for platforms that support misaligned memory access.
- /// </remarks>
- /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source)
- where TFrom : struct
- where TTo : struct
+ public static bool StartsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
-
- return new Span<TTo>(
- ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ Debug.Assert(value.Length != 0);
+
+ if (GlobalizationMode.Invariant)
+ {
+ return StartsWithOrdinalIgnoreCaseHelper(span, value);
+ }
+ if (span.Length == 0)
+ {
+ return false;
+ }
+ return compareInfo.IsPrefix(span, value, CompareOptions.IgnoreCase);
}
- /// <summary>
- /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <remarks>
- /// Supported only for platforms that support misaligned memory access.
- /// </remarks>
- /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TFrom> source)
- where TFrom : struct
- where TTo : struct
+ public static bool StartsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
{
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
-
- return new ReadOnlySpan<TTo>(
- ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ Debug.Assert(value.Length != 0);
+
+ if (span.Length < value.Length)
+ {
+ return false;
+ }
+ return CompareInfo.CompareOrdinalIgnoreCase(span.Slice(0, value.Length), value) == 0;
}
- /// <summary>
- /// Creates a new readonly span over the portion of the target string.
- /// </summary>
- /// <param name="text">The target string.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> AsReadOnlySpan(this string text)
+ public static bool EndsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (text == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+ Debug.Assert(value.Length != 0);
- return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
+ if (GlobalizationMode.Invariant)
+ {
+ return span.EndsWith(value);
+ }
+ if (span.Length == 0)
+ {
+ return false;
+ }
+ return compareInfo.IsSuffix(span, value, CompareOptions.None);
}
- internal static unsafe void CopyTo<T>(ref T destination, ref T source, int elementsCount)
+ public static bool EndsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
{
- if (Unsafe.AreSame(ref destination, ref source))
- return;
+ Debug.Assert(value.Length != 0);
- if (elementsCount <= 1)
+ if (GlobalizationMode.Invariant)
{
- if (elementsCount == 1)
- {
- destination = source;
- }
- return;
+ return EndsWithOrdinalIgnoreCaseHelper(span, value);
}
-
- nuint byteCount = (nuint)elementsCount * (nuint)Unsafe.SizeOf<T>();
- if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ if (span.Length == 0)
{
- fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination))
- {
- fixed (byte* pSource = &Unsafe.As<T, byte>(ref source))
- {
- Buffer.Memmove(pDestination, pSource, byteCount);
- }
- }
+ return false;
}
- else
+ return compareInfo.IsSuffix(span, value, CompareOptions.IgnoreCase);
+ }
+
+ public static bool EndsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
+ {
+ Debug.Assert(value.Length != 0);
+
+ if (span.Length < value.Length)
{
- RuntimeImports.RhBulkMoveWithWriteBarrier(
- ref Unsafe.As<T, byte>(ref destination),
- ref Unsafe.As<T, byte>(ref source),
- byteCount);
+ return false;
}
+ return (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(span.Length - value.Length), value) == 0);
}
- internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
+ public static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
{
if (byteLength == 0)
return;
-
+
#if CORECLR && (AMD64 || ARM64)
- if (byteLength > 4096) goto PInvoke;
+ if (byteLength > 4096)
+ goto PInvoke;
Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
return;
#else
@@ -513,12 +454,12 @@ namespace System
return;
#endif
-
- PInvoke:
+
+ PInvoke:
RuntimeImports.RhZeroMemory(ref b, byteLength);
}
- internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
+ public static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
{
if (pointerSizeLength == 0)
return;
diff --git a/src/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/System.Private.CoreLib/shared/System/String.Manipulation.cs
index f069b3942..76cbfaa8d 100644
--- a/src/System.Private.CoreLib/src/System/String.Manipulation.cs
+++ b/src/System.Private.CoreLib/shared/System/String.Manipulation.cs
@@ -2,20 +2,25 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
-
using Internal.Runtime.CompilerServices;
namespace System
{
public partial class String
{
- unsafe private static void FillStringChecked(String dest, int destPos, String src)
+ private const int StackallocIntBufferSizeLimit = 128;
+
+ private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
+ Debug.Assert(dest != null);
+ Debug.Assert(src != null);
if (src.Length > dest.Length - destPos)
{
throw new IndexOutOfRangeException();
@@ -28,44 +33,44 @@ namespace System
}
}
- public static String Concat(Object arg0)
+ public static string Concat(object arg0)
{
if (arg0 == null)
{
- return String.Empty;
+ return string.Empty;
}
return arg0.ToString();
}
- public static String Concat(Object arg0, Object arg1)
+ public static string Concat(object arg0, object arg1)
{
if (arg0 == null)
{
- arg0 = String.Empty;
+ arg0 = string.Empty;
}
if (arg1 == null)
{
- arg1 = String.Empty;
+ arg1 = string.Empty;
}
return Concat(arg0.ToString(), arg1.ToString());
}
- public static String Concat(Object arg0, Object arg1, Object arg2)
+ public static string Concat(object arg0, object arg1, object arg2)
{
if (arg0 == null)
{
- arg0 = String.Empty;
+ arg0 = string.Empty;
}
if (arg1 == null)
{
- arg1 = String.Empty;
+ arg1 = string.Empty;
}
if (arg2 == null)
{
- arg2 = String.Empty;
+ arg2 = string.Empty;
}
return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString());
@@ -250,13 +255,13 @@ namespace System
}
}
- public static String Concat(String str0, String str1)
+ public static string Concat(string str0, string str1)
{
if (IsNullOrEmpty(str0))
{
if (IsNullOrEmpty(str1))
{
- return String.Empty;
+ return string.Empty;
}
return str1;
}
@@ -268,7 +273,7 @@ namespace System
int str0Length = str0.Length;
- String result = FastAllocateString(str0Length + str1.Length);
+ string result = FastAllocateString(str0Length + str1.Length);
FillStringChecked(result, 0, str0);
FillStringChecked(result, str0Length, str1);
@@ -276,7 +281,7 @@ namespace System
return result;
}
- public static String Concat(String str0, String str1, String str2)
+ public static string Concat(string str0, string str1, string str2)
{
if (IsNullOrEmpty(str0))
{
@@ -295,7 +300,7 @@ namespace System
int totalLength = str0.Length + str1.Length + str2.Length;
- String result = FastAllocateString(totalLength);
+ string result = FastAllocateString(totalLength);
FillStringChecked(result, 0, str0);
FillStringChecked(result, str0.Length, str1);
FillStringChecked(result, str0.Length + str1.Length, str2);
@@ -303,7 +308,7 @@ namespace System
return result;
}
- public static String Concat(String str0, String str1, String str2, String str3)
+ public static string Concat(string str0, string str1, string str2, string str3)
{
if (IsNullOrEmpty(str0))
{
@@ -327,7 +332,7 @@ namespace System
int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;
- String result = FastAllocateString(totalLength);
+ string result = FastAllocateString(totalLength);
FillStringChecked(result, 0, str0);
FillStringChecked(result, str0.Length, str1);
FillStringChecked(result, str0.Length + str1.Length, str2);
@@ -336,7 +341,7 @@ namespace System
return result;
}
- public static String Concat(params String[] values)
+ public static string Concat(params string[] values)
{
if (values == null)
throw new ArgumentNullException(nameof(values));
@@ -404,34 +409,22 @@ namespace System
return copiedLength == totalLength ? result : Concat((string[])values.Clone());
}
- public static String Format(String format, params Object[] args)
- {
- if (args == null)
- {
- // To preserve the original exception behavior, throw an exception about format if both
- // args and format are null. The actual null check for format is in FormatHelper.
- throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
- }
-
- return FormatHelper(null, format, new ParamsArray(args));
- }
-
- public static String Format(String format, Object arg0)
+ public static string Format(string format, object arg0)
{
return FormatHelper(null, format, new ParamsArray(arg0));
}
- public static String Format(String format, Object arg0, Object arg1)
+ public static string Format(string format, object arg0, object arg1)
{
return FormatHelper(null, format, new ParamsArray(arg0, arg1));
}
- public static String Format(String format, Object arg0, Object arg1, Object arg2)
+ public static string Format(string format, object arg0, object arg1, object arg2)
{
return FormatHelper(null, format, new ParamsArray(arg0, arg1, arg2));
}
- public static String Format(IFormatProvider provider, String format, params Object[] args)
+ public static string Format(string format, params object[] args)
{
if (args == null)
{
@@ -440,25 +433,37 @@ namespace System
throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
}
- return FormatHelper(provider, format, new ParamsArray(args));
+ return FormatHelper(null, format, new ParamsArray(args));
}
- public static String Format(IFormatProvider provider, String format, Object arg0)
+ public static string Format(IFormatProvider provider, string format, object arg0)
{
return FormatHelper(provider, format, new ParamsArray(arg0));
}
- public static String Format(IFormatProvider provider, String format, Object arg0, Object arg1)
+ public static string Format(IFormatProvider provider, string format, object arg0, object arg1)
{
return FormatHelper(provider, format, new ParamsArray(arg0, arg1));
}
- public static String Format(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2)
+ public static string Format(IFormatProvider provider, string format, object arg0, object arg1, object arg2)
{
return FormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2));
}
- private static String FormatHelper(IFormatProvider provider, String format, ParamsArray args)
+ public static string Format(IFormatProvider provider, string format, params object[] args)
+ {
+ if (args == null)
+ {
+ // To preserve the original exception behavior, throw an exception about format if both
+ // args and format are null. The actual null check for format is in FormatHelper.
+ throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
+ }
+
+ return FormatHelper(provider, format, new ParamsArray(args));
+ }
+
+ private static string FormatHelper(IFormatProvider provider, string format, ParamsArray args)
{
if (format == null)
throw new ArgumentNullException(nameof(format));
@@ -469,7 +474,7 @@ namespace System
.AppendFormatHelper(provider, format, args));
}
- public String Insert(int startIndex, String value)
+ public string Insert(int startIndex, string value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
@@ -484,10 +489,9 @@ namespace System
if (insertLength == 0)
return this;
+ // In case this computation overflows, newLength will be negative and FastAllocateString throws OutOfMemoryException
int newLength = oldLength + insertLength;
- if (newLength < 0)
- throw new OutOfMemoryException();
- String result = FastAllocateString(newLength);
+ string result = FastAllocateString(newLength);
unsafe
{
fixed (char* srcThis = &_firstChar)
@@ -810,12 +814,9 @@ namespace System
JoinCore(separator, separatorLength, (string[])value.Clone(), startIndex, count);
}
- public String PadLeft(int totalWidth)
- {
- return PadLeft(totalWidth, ' ');
- }
+ public string PadLeft(int totalWidth) => PadLeft(totalWidth, ' ');
- public String PadLeft(int totalWidth, char paddingChar)
+ public string PadLeft(int totalWidth, char paddingChar)
{
if (totalWidth < 0)
throw new ArgumentOutOfRangeException(nameof(totalWidth), SR.ArgumentOutOfRange_NeedNonNegNum);
@@ -823,7 +824,7 @@ namespace System
int count = totalWidth - oldLength;
if (count <= 0)
return this;
- String result = FastAllocateString(totalWidth);
+ string result = FastAllocateString(totalWidth);
unsafe
{
fixed (char* dst = &result._firstChar)
@@ -839,12 +840,9 @@ namespace System
return result;
}
- public String PadRight(int totalWidth)
- {
- return PadRight(totalWidth, ' ');
- }
+ public string PadRight(int totalWidth) => PadRight(totalWidth, ' ');
- public String PadRight(int totalWidth, char paddingChar)
+ public string PadRight(int totalWidth, char paddingChar)
{
if (totalWidth < 0)
throw new ArgumentOutOfRangeException(nameof(totalWidth), SR.ArgumentOutOfRange_NeedNonNegNum);
@@ -852,7 +850,7 @@ namespace System
int count = totalWidth - oldLength;
if (count <= 0)
return this;
- String result = FastAllocateString(totalWidth);
+ string result = FastAllocateString(totalWidth);
unsafe
{
fixed (char* dst = &result._firstChar)
@@ -868,7 +866,7 @@ namespace System
return result;
}
- public String Remove(int startIndex, int count)
+ public string Remove(int startIndex, int count)
{
if (startIndex < 0)
throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
@@ -884,7 +882,7 @@ namespace System
if (newLength == 0)
return string.Empty;
- String result = FastAllocateString(newLength);
+ string result = FastAllocateString(newLength);
unsafe
{
fixed (char* src = &_firstChar)
@@ -903,16 +901,10 @@ namespace System
public string Remove(int startIndex)
{
if (startIndex < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(startIndex),
- SR.ArgumentOutOfRange_StartIndex);
- }
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
if (startIndex >= Length)
- {
- throw new ArgumentOutOfRangeException(nameof(startIndex),
- SR.ArgumentOutOfRange_StartIndexLessThanLength);
- }
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLessThanLength);
return Substring(0, startIndex);
}
@@ -939,7 +931,7 @@ namespace System
return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
- return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.Ordinal);
+ return Replace(oldValue, newValue);
case StringComparison.OrdinalIgnoreCase:
return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase);
@@ -949,7 +941,7 @@ namespace System
}
}
- private unsafe String ReplaceCore(string oldValue, string newValue, CultureInfo culture, CompareOptions options)
+ private unsafe string ReplaceCore(string oldValue, string newValue, CultureInfo culture, CompareOptions options)
{
if (oldValue == null)
throw new ArgumentNullException(nameof(oldValue));
@@ -974,7 +966,7 @@ namespace System
do
{
- index = ci.IndexOf(this, oldValue, startIndex, _stringLength - startIndex, options, &matchLength);
+ index = ci.IndexOf(this, oldValue, startIndex, this.Length - startIndex, options, &matchLength);
if (index >= 0)
{
// append the unmodified portion of string
@@ -991,11 +983,12 @@ namespace System
// small optimization,
// if we have not done any replacements,
// we will return the original string
+ StringBuilderCache.Release(result);
return this;
}
else
{
- result.Append(this, startIndex, _stringLength - startIndex);
+ result.Append(this, startIndex, this.Length - startIndex);
}
} while (index >= 0);
@@ -1004,7 +997,7 @@ namespace System
// Replaces all instances of oldChar with newChar.
//
- public String Replace(char oldChar, char newChar)
+ public string Replace(char oldChar, char newChar)
{
if (oldChar == newChar)
return this;
@@ -1032,7 +1025,7 @@ namespace System
if (remainingLength == 0)
return this;
- String result = FastAllocateString(Length);
+ string result = FastAllocateString(Length);
fixed (char* pChars = &_firstChar)
{
@@ -1068,117 +1061,106 @@ namespace System
}
}
- public String Replace(String oldValue, String newValue)
+ public string Replace(string oldValue, string newValue)
{
+ if (oldValue == null)
+ throw new ArgumentNullException(nameof(oldValue));
+ if (oldValue.Length == 0)
+ throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue));
+
+ // Api behavior: if newValue is null, instances of oldValue are to be removed.
+ if (newValue == null)
+ newValue = string.Empty;
+
+ Span<int> initialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+ var replacementIndices = new ValueListBuilder<int>(initialSpan);
+
unsafe
{
- if (oldValue == null)
- throw new ArgumentNullException(nameof(oldValue));
- if (oldValue.Length == 0)
- throw new ArgumentException(SR.Format(SR.Argument_StringZeroLength, nameof(oldValue)));
- // Api behavior: if newValue is null, instances of oldValue are to be removed.
- if (newValue == null)
- newValue = String.Empty;
-
- int numOccurrences = 0;
- int[] replacementIndices = new int[this.Length];
fixed (char* pThis = &_firstChar)
{
- fixed (char* pOldValue = &oldValue._firstChar)
+ int matchIdx = 0;
+ int lastPossibleMatchIdx = this.Length - oldValue.Length;
+ while (matchIdx <= lastPossibleMatchIdx)
{
- int idx = 0;
- int lastPossibleMatchIdx = this.Length - oldValue.Length;
- while (idx <= lastPossibleMatchIdx)
+ char* pMatch = pThis + matchIdx;
+ for (int probeIdx = 0; probeIdx < oldValue.Length; probeIdx++)
{
- int probeIdx = idx;
- int oldValueIdx = 0;
- bool foundMismatch = false;
- while (oldValueIdx < oldValue.Length)
- {
- Debug.Assert(probeIdx >= 0 && probeIdx < this.Length);
- Debug.Assert(oldValueIdx >= 0 && oldValueIdx < oldValue.Length);
- if (pThis[probeIdx] != pOldValue[oldValueIdx])
- {
- foundMismatch = true;
- break;
- }
- probeIdx++;
- oldValueIdx++;
- }
- if (!foundMismatch)
+ if (pMatch[probeIdx] != oldValue[probeIdx])
{
- // Found a match for the string. Record the location of the match and skip over the "oldValue."
- replacementIndices[numOccurrences++] = idx;
- Debug.Assert(probeIdx == idx + oldValue.Length);
- idx = probeIdx;
- }
- else
- {
- idx++;
+ goto Next;
}
}
+ // Found a match for the string. Record the location of the match and skip over the "oldValue."
+ replacementIndices.Append(matchIdx);
+ matchIdx += oldValue.Length;
+ continue;
+
+ Next:
+ matchIdx++;
}
}
+ }
- if (numOccurrences == 0)
- return this;
+ if (replacementIndices.Length == 0)
+ return this;
- int dstLength = checked(this.Length + (newValue.Length - oldValue.Length) * numOccurrences);
- String dst = FastAllocateString(dstLength);
- fixed (char* pThis = &_firstChar)
- {
- fixed (char* pDst = &dst._firstChar)
- {
- fixed (char* pNewValue = &newValue._firstChar)
- {
- int dstIdx = 0;
- int thisIdx = 0;
+ // String allocation and copying is in separate method to make this method faster for the case where
+ // nothing needs replacing.
+ string dst = ReplaceHelper(oldValue.Length, newValue, replacementIndices.AsSpan());
- for (int r = 0; r < numOccurrences; r++)
- {
- int replacementIdx = replacementIndices[r];
-
- // Copy over the non-matching portion of the original that precedes this occurrence of oldValue.
- int count = replacementIdx - thisIdx;
- Debug.Assert(count >= 0);
- Debug.Assert(thisIdx >= 0 && thisIdx <= this.Length - count);
- Debug.Assert(dstIdx >= 0 && dstIdx <= dst.Length - count);
- if (count != 0)
- {
- wstrcpy(&(pDst[dstIdx]), &(pThis[thisIdx]), count);
- dstIdx += count;
- }
- thisIdx = replacementIdx + oldValue.Length;
-
- // Copy over newValue to replace the oldValue.
- Debug.Assert(thisIdx >= 0 && thisIdx <= this.Length);
- Debug.Assert(dstIdx >= 0 && dstIdx <= dst.Length - newValue.Length);
- wstrcpy(&(pDst[dstIdx]), pNewValue, newValue.Length);
- dstIdx += newValue.Length;
- }
+ replacementIndices.Dispose();
- // Copy over the final non-matching portion at the end of the string.
- int tailLength = this.Length - thisIdx;
- Debug.Assert(tailLength >= 0);
- Debug.Assert(thisIdx == this.Length - tailLength);
- Debug.Assert(dstIdx == dst.Length - tailLength);
- wstrcpy(&(pDst[dstIdx]), &(pThis[thisIdx]), tailLength);
- }
- }
+ return dst;
+ }
+
+ private string ReplaceHelper(int oldValueLength, string newValue, ReadOnlySpan<int> indices)
+ {
+ Debug.Assert(indices.Length > 0);
+
+ long dstLength = this.Length + ((long)(newValue.Length - oldValueLength)) * indices.Length;
+ if (dstLength > int.MaxValue)
+ throw new OutOfMemoryException();
+ string dst = FastAllocateString((int)dstLength);
+
+ Span<char> dstSpan = new Span<char>(ref dst.GetRawStringData(), dst.Length);
+
+ int thisIdx = 0;
+ int dstIdx = 0;
+
+ for (int r = 0; r < indices.Length; r++)
+ {
+ int replacementIdx = indices[r];
+
+ // Copy over the non-matching portion of the original that precedes this occurrence of oldValue.
+ int count = replacementIdx - thisIdx;
+ if (count != 0)
+ {
+ this.AsSpan().Slice(thisIdx, count).CopyTo(dstSpan.Slice(dstIdx));
+ dstIdx += count;
}
+ thisIdx = replacementIdx + oldValueLength;
- return dst;
+ // Copy over newValue to replace the oldValue.
+ newValue.AsSpan().CopyTo(dstSpan.Slice(dstIdx));
+ dstIdx += newValue.Length;
}
+
+ // Copy over the final non-matching portion at the end of the string.
+ Debug.Assert(this.Length - thisIdx == dstSpan.Length - dstIdx);
+ this.AsSpan().Slice(thisIdx).CopyTo(dstSpan.Slice(dstIdx));
+
+ return dst;
}
- public unsafe String[] Split(char separator, StringSplitOptions options = StringSplitOptions.None)
+ public string[] Split(char separator, StringSplitOptions options = StringSplitOptions.None)
{
- return SplitInternal(&separator, 1, int.MaxValue, options);
+ return SplitInternal(new ReadOnlySpan<char>(ref separator, 1), int.MaxValue, options);
}
- public unsafe String[] Split(char separator, int count, StringSplitOptions options = StringSplitOptions.None)
+ public string[] Split(char separator, int count, StringSplitOptions options = StringSplitOptions.None)
{
- return SplitInternal(&separator, 1, count, options);
+ return SplitInternal(new ReadOnlySpan<char>(ref separator, 1), count, options);
}
// Creates an array of strings by splitting this string at each
@@ -1190,9 +1172,9 @@ namespace System
// If the separator is null
// whitespace (i.e., Character.IsWhitespace) is used as the separator.
//
- public String[] Split(params char[] separator)
+ public string[] Split(params char[] separator)
{
- return SplitInternal(separator, Int32.MaxValue, StringSplitOptions.None);
+ return SplitInternal(separator, int.MaxValue, StringSplitOptions.None);
}
// Creates an array of strings by splitting this string at each
@@ -1201,36 +1183,27 @@ namespace System
// the array of strings. We then continue in this manner by searching
// the substring that follows the occurrence. On the other hand, if the separator
// is not found, the array of strings will contain this instance as its only element.
- // If the separator is the empty string (i.e., String.Empty), then
+ // If the separator is the empty string (i.e., string.Empty), then
// whitespace (i.e., Character.IsWhitespace) is used as the separator.
// If there are more than count different strings, the last n-(count-1)
- // elements are concatenated and added as the last String.
+ // elements are concatenated and added as the last string.
//
public string[] Split(char[] separator, int count)
{
return SplitInternal(separator, count, StringSplitOptions.None);
}
- public String[] Split(char[] separator, StringSplitOptions options)
+ public string[] Split(char[] separator, StringSplitOptions options)
{
- return SplitInternal(separator, Int32.MaxValue, options);
+ return SplitInternal(separator, int.MaxValue, options);
}
- public String[] Split(char[] separator, int count, StringSplitOptions options)
+ public string[] Split(char[] separator, int count, StringSplitOptions options)
{
return SplitInternal(separator, count, options);
}
- private unsafe String[] SplitInternal(char[] separator, int count, StringSplitOptions options)
- {
- fixed (char* pSeparators = separator)
- {
- int separatorsLength = separator == null ? 0 : separator.Length;
- return SplitInternal(pSeparators, separatorsLength, count, options);
- }
- }
-
- private unsafe String[] SplitInternal(char* separators, int separatorsLength, int count, StringSplitOptions options)
+ private string[] SplitInternal(ReadOnlySpan<char> separators, int count, StringSplitOptions options)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count),
@@ -1241,56 +1214,58 @@ namespace System
bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);
- if ((count == 0) || (omitEmptyEntries && this.Length == 0))
+ if ((count == 0) || (omitEmptyEntries && Length == 0))
{
- return Array.Empty<String>();
+ return Array.Empty<string>();
}
if (count == 1)
{
- return new String[] { this };
+ return new string[] { this };
}
- int[] sepList = new int[Length];
- int numReplaces = MakeSeparatorList(separators, separatorsLength, sepList);
+ Span<int> initialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+ var sepListBuilder = new ValueListBuilder<int>(initialSpan);
+
+ MakeSeparatorList(separators, ref sepListBuilder);
+ ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
// Handle the special case of no replaces.
- if (0 == numReplaces)
+ if (sepList.Length == 0)
{
- return new String[] { this };
+ return new string[] { this };
}
- if (omitEmptyEntries)
- {
- return SplitOmitEmptyEntries(sepList, null, 1, numReplaces, count);
- }
- else
- {
- return SplitKeepEmptyEntries(sepList, null, 1, numReplaces, count);
- }
+ string[] result = omitEmptyEntries
+ ? SplitOmitEmptyEntries(sepList, default, 1, count)
+ : SplitKeepEmptyEntries(sepList, default, 1, count);
+
+ sepListBuilder.Dispose();
+
+ return result;
}
- public String[] Split(String separator, StringSplitOptions options = StringSplitOptions.None)
+ public string[] Split(string separator, StringSplitOptions options = StringSplitOptions.None)
{
- return SplitInternal(separator ?? String.Empty, null, Int32.MaxValue, options);
+ return SplitInternal(separator ?? string.Empty, null, int.MaxValue, options);
}
- public String[] Split(String separator, Int32 count, StringSplitOptions options = StringSplitOptions.None)
+ public string[] Split(string separator, Int32 count, StringSplitOptions options = StringSplitOptions.None)
{
- return SplitInternal(separator ?? String.Empty, null, count, options);
+ return SplitInternal(separator ?? string.Empty, null, count, options);
}
- public String[] Split(String[] separator, StringSplitOptions options)
+ public string[] Split(string[] separator, StringSplitOptions options)
{
- return SplitInternal(null, separator, Int32.MaxValue, options);
+ return SplitInternal(null, separator, int.MaxValue, options);
}
- public String[] Split(String[] separator, Int32 count, StringSplitOptions options)
+ public string[] Split(string[] separator, Int32 count, StringSplitOptions options)
{
return SplitInternal(null, separator, count, options);
}
- private String[] SplitInternal(String separator, String[] separators, Int32 count, StringSplitOptions options)
+ private string[] SplitInternal(string separator, string[] separators, int count, StringSplitOptions options)
{
if (count < 0)
{
@@ -1312,74 +1287,87 @@ namespace System
return SplitInternal((char[])null, count, options);
}
- if ((count == 0) || (omitEmptyEntries && this.Length == 0))
+ if ((count == 0) || (omitEmptyEntries && Length == 0))
{
- return Array.Empty<String>();
+ return Array.Empty<string>();
}
if (count == 1 || (singleSeparator && separator.Length == 0))
{
- return new String[] { this };
+ return new string[] { this };
}
- int[] sepList = new int[Length];
- int[] lengthList;
- int defaultLength;
- int numReplaces;
-
if (singleSeparator)
{
- lengthList = null;
- defaultLength = separator.Length;
- numReplaces = MakeSeparatorList(separator, sepList);
- }
- else
- {
- lengthList = new int[Length];
- defaultLength = 0;
- numReplaces = MakeSeparatorList(separators, sepList, lengthList);
+ return SplitInternal(separator, count, options);
}
+
+ Span<int> sepListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+ var sepListBuilder = new ValueListBuilder<int>(sepListInitialSpan);
+
+ Span<int> lengthListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+ var lengthListBuilder = new ValueListBuilder<int>(lengthListInitialSpan);
+ MakeSeparatorList(separators, ref sepListBuilder, ref lengthListBuilder);
+ ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
+ ReadOnlySpan<int> lengthList = lengthListBuilder.AsSpan();
+
// Handle the special case of no replaces.
- if (0 == numReplaces)
+ if (sepList.Length == 0)
{
- return new String[] { this };
+ return new string[] { this };
}
- if (omitEmptyEntries)
- {
- return SplitOmitEmptyEntries(sepList, lengthList, defaultLength, numReplaces, count);
- }
- else
+ string[] result = omitEmptyEntries
+ ? SplitOmitEmptyEntries(sepList, lengthList, 0, count)
+ : SplitKeepEmptyEntries(sepList, lengthList, 0, count);
+
+ sepListBuilder.Dispose();
+ lengthListBuilder.Dispose();
+
+ return result;
+ }
+
+ private string[] SplitInternal(string separator, int count, StringSplitOptions options)
+ {
+ Span<int> sepListInitialSpan = stackalloc int[StackallocIntBufferSizeLimit];
+ var sepListBuilder = new ValueListBuilder<int>(sepListInitialSpan);
+
+ MakeSeparatorList(separator, ref sepListBuilder);
+ ReadOnlySpan<int> sepList = sepListBuilder.AsSpan();
+ if (sepList.Length == 0)
{
- return SplitKeepEmptyEntries(sepList, lengthList, defaultLength, numReplaces, count);
+ // there are no separators so sepListBuilder did not rent an array from pool and there is no need to dispose it
+ return new string[] { this };
}
- }
- // Note a special case in this function:
- // If there is no separator in the string, a string array which only contains
- // the original string will be returned regardless of the count.
- //
+ string[] result = options == StringSplitOptions.RemoveEmptyEntries
+ ? SplitOmitEmptyEntries(sepList, default, separator.Length, count)
+ : SplitKeepEmptyEntries(sepList, default, separator.Length, count);
+
+ sepListBuilder.Dispose();
+
+ return result;
+ }
- private String[] SplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 defaultLength, Int32 numReplaces, int count)
+ private string[] SplitKeepEmptyEntries(ReadOnlySpan<int> sepList, ReadOnlySpan<int> lengthList, int defaultLength, int count)
{
- Debug.Assert(numReplaces >= 0);
Debug.Assert(count >= 2);
int currIndex = 0;
int arrIndex = 0;
count--;
- int numActualReplaces = (numReplaces < count) ? numReplaces : count;
+ int numActualReplaces = (sepList.Length < count) ? sepList.Length : count;
//Allocate space for the new array.
- //+1 for the string from the end of the last replace to the end of the String.
- String[] splitStrings = new String[numActualReplaces + 1];
+ //+1 for the string from the end of the last replace to the end of the string.
+ string[] splitStrings = new string[numActualReplaces + 1];
for (int i = 0; i < numActualReplaces && currIndex < Length; i++)
{
splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
- currIndex = sepList[i] + ((lengthList == null) ? defaultLength : lengthList[i]);
+ currIndex = sepList[i] + (lengthList.IsEmpty ? defaultLength : lengthList[i]);
}
//Handle the last string at the end of the array if there is one.
@@ -1391,25 +1379,25 @@ namespace System
{
//We had a separator character at the end of a string. Rather than just allowing
//a null character, we'll replace the last element in the array with an empty string.
- splitStrings[arrIndex] = String.Empty;
+ splitStrings[arrIndex] = string.Empty;
}
return splitStrings;
}
- // This function will not keep the Empty String
- private String[] SplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 defaultLength, Int32 numReplaces, int count)
+ // This function will not keep the Empty string
+ private string[] SplitOmitEmptyEntries(ReadOnlySpan<int> sepList, ReadOnlySpan<int> lengthList, int defaultLength, int count)
{
- Debug.Assert(numReplaces >= 0);
Debug.Assert(count >= 2);
+ int numReplaces = sepList.Length;
+
// Allocate array to hold items. This array may not be
// filled completely in this function, we will create a
// new array and copy string references to that new array.
-
int maxItems = (numReplaces < count) ? (numReplaces + 1) : count;
- String[] splitStrings = new String[maxItems];
+ string[] splitStrings = new string[maxItems];
int currIndex = 0;
int arrIndex = 0;
@@ -1420,13 +1408,13 @@ namespace System
{
splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
}
- currIndex = sepList[i] + ((lengthList == null) ? defaultLength : lengthList[i]);
+ currIndex = sepList[i] + (lengthList.IsEmpty ? defaultLength : lengthList[i]);
if (arrIndex == count - 1)
{
// If all the remaining entries at the end are empty, skip them
while (i < numReplaces - 1 && currIndex == sepList[++i])
{
- currIndex += ((lengthList == null) ? defaultLength : lengthList[i]);
+ currIndex += (lengthList.IsEmpty ? defaultLength : lengthList[i]);
}
break;
}
@@ -1441,10 +1429,10 @@ namespace System
splitStrings[arrIndex++] = Substring(currIndex);
}
- String[] stringArray = splitStrings;
+ string[] stringArray = splitStrings;
if (arrIndex != maxItems)
{
- stringArray = new String[arrIndex];
+ stringArray = new string[arrIndex];
for (int j = 0; j < arrIndex; j++)
{
stringArray[j] = splitStrings[j];
@@ -1453,145 +1441,158 @@ namespace System
return stringArray;
}
- //--------------------------------------------------------------------
- // This function returns the number of the places within this instance where
- // characters in Separator occur.
- // Args: separator -- A string containing all of the split characters.
- // sepList -- an array of ints for split char indicies.
- //--------------------------------------------------------------------
- private unsafe int MakeSeparatorList(char* separators, int separatorsLength, int[] sepList)
+ /// <summary>
+ /// Uses ValueListBuilder to create list that holds indexes of separators in string.
+ /// </summary>
+ /// <param name="separators"><see cref="ReadOnlySpan{T}"/> of separator chars</param>
+ /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> to store indexes</param>
+ /// <returns></returns>
+ private void MakeSeparatorList(ReadOnlySpan<char> separators, ref ValueListBuilder<int> sepListBuilder)
{
- Debug.Assert(separatorsLength >= 0, "separatorsLength >= 0");
+ char sep0, sep1, sep2;
- int foundCount = 0;
-
- if (separators == null || separatorsLength == 0)
+ switch (separators.Length)
{
- fixed (char* pwzChars = &_firstChar)
- {
- //If they passed null or an empty string, look for whitespace.
- for (int i = 0; i < Length && foundCount < sepList.Length; i++)
+ // Special-case no separators to mean any whitespace is a separator.
+ case 0:
+ for (int i = 0; i < Length; i++)
{
- if (Char.IsWhiteSpace(pwzChars[i]))
+ if (char.IsWhiteSpace(this[i]))
{
- sepList[foundCount++] = i;
+ sepListBuilder.Append(i);
}
}
- }
- }
- else
- {
- int sepListCount = sepList.Length;
- //If they passed in a string of chars, actually look for those chars.
- fixed (char* pwzChars = &_firstChar)
- {
- for (int i = 0; i < Length && foundCount < sepListCount; i++)
+ break;
+
+ // Special-case the common cases of 1, 2, and 3 separators, with manual comparisons against each separator.
+ case 1:
+ sep0 = separators[0];
+ for (int i = 0; i < Length; i++)
{
- char* pSep = separators;
- for (int j = 0; j < separatorsLength; j++, pSep++)
+ if (this[i] == sep0)
{
- if (pwzChars[i] == *pSep)
+ sepListBuilder.Append(i);
+ }
+ }
+ break;
+ case 2:
+ sep0 = separators[0];
+ sep1 = separators[1];
+ for (int i = 0; i < Length; i++)
+ {
+ char c = this[i];
+ if (c == sep0 || c == sep1)
+ {
+ sepListBuilder.Append(i);
+ }
+ }
+ break;
+ case 3:
+ sep0 = separators[0];
+ sep1 = separators[1];
+ sep2 = separators[2];
+ for (int i = 0; i < Length; i++)
+ {
+ char c = this[i];
+ if (c == sep0 || c == sep1 || c == sep2)
+ {
+ sepListBuilder.Append(i);
+ }
+ }
+ break;
+
+ // Handle > 3 separators with a probabilistic map, ala IndexOfAny.
+ // This optimizes for chars being unlikely to match a separator.
+ default:
+ unsafe
+ {
+ ProbabilisticMap map = default;
+ uint* charMap = (uint*)&map;
+ InitializeProbabilisticMap(charMap, separators);
+
+ for (int i = 0; i < Length; i++)
+ {
+ char c = this[i];
+ if (IsCharBitSet(charMap, (byte)c) && IsCharBitSet(charMap, (byte)(c >> 8)) &&
+ separators.Contains(c))
{
- sepList[foundCount++] = i;
- break;
+ sepListBuilder.Append(i);
}
}
}
- }
+ break;
}
- return foundCount;
}
- //--------------------------------------------------------------------
- // This function returns number of the places within baseString where
- // instances of the separator string occurs.
- // Args: separator -- the separator
- // sepList -- an array of ints for split string indicies.
- //--------------------------------------------------------------------
- private unsafe int MakeSeparatorList(string separator, int[] sepList)
+ /// <summary>
+ /// Uses ValueListBuilder to create list that holds indexes of separators in string.
+ /// </summary>
+ /// <param name="separator">separator string</param>
+ /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> to store indexes</param>
+ /// <returns></returns>
+ private void MakeSeparatorList(string separator, ref ValueListBuilder<int> sepListBuilder)
{
- Debug.Assert(!string.IsNullOrEmpty(separator), "!string.IsNullOrEmpty(separator)");
+ Debug.Assert(!IsNullOrEmpty(separator), "!string.IsNullOrEmpty(separator)");
- int foundCount = 0;
- int sepListCount = sepList.Length;
int currentSepLength = separator.Length;
- fixed (char* pwzChars = &_firstChar)
+ for (int i = 0; i < Length; i++)
{
- for (int i = 0; i < Length && foundCount < sepListCount; i++)
+ if (this[i] == separator[0] && currentSepLength <= Length - i)
{
- if (pwzChars[i] == separator[0] && currentSepLength <= Length - i)
+ if (currentSepLength == 1
+ || CompareOrdinal(this, i, separator, 0, currentSepLength) == 0)
{
- if (currentSepLength == 1
- || String.CompareOrdinal(this, i, separator, 0, currentSepLength) == 0)
- {
- sepList[foundCount] = i;
- foundCount++;
- i += currentSepLength - 1;
- }
+ sepListBuilder.Append(i);
+ i += currentSepLength - 1;
}
}
}
- return foundCount;
}
- //--------------------------------------------------------------------
- // This function returns the number of the places within this instance where
- // instances of separator strings occur.
- // Args: separators -- An array containing all of the split strings.
- // sepList -- an array of ints for split string indicies.
- // lengthList -- an array of ints for split string lengths.
- //--------------------------------------------------------------------
- private unsafe int MakeSeparatorList(String[] separators, int[] sepList, int[] lengthList)
+ /// <summary>
+ /// Uses ValueListBuilder to create list that holds indexes of separators in string and list that holds length of separator strings.
+ /// </summary>
+ /// <param name="separators">separator strngs</param>
+ /// <param name="sepListBuilder"><see cref="ValueListBuilder{T}"/> for separator indexes</param>
+ /// <param name="lengthListBuilder"><see cref="ValueListBuilder{T}"/> for separator length values</param>
+ private void MakeSeparatorList(string[] separators, ref ValueListBuilder<int> sepListBuilder, ref ValueListBuilder<int> lengthListBuilder)
{
Debug.Assert(separators != null && separators.Length > 0, "separators != null && separators.Length > 0");
- int foundCount = 0;
- int sepListCount = sepList.Length;
int sepCount = separators.Length;
- fixed (char* pwzChars = &_firstChar)
+ for (int i = 0; i < Length; i++)
{
- for (int i = 0; i < Length && foundCount < sepListCount; i++)
+ for (int j = 0; j < separators.Length; j++)
{
- for (int j = 0; j < separators.Length; j++)
+ string separator = separators[j];
+ if (IsNullOrEmpty(separator))
{
- String separator = separators[j];
- if (String.IsNullOrEmpty(separator))
- {
- continue;
- }
- Int32 currentSepLength = separator.Length;
- if (pwzChars[i] == separator[0] && currentSepLength <= Length - i)
+ continue;
+ }
+ int currentSepLength = separator.Length;
+ if (this[i] == separator[0] && currentSepLength <= Length - i)
+ {
+ if (currentSepLength == 1
+ || CompareOrdinal(this, i, separator, 0, currentSepLength) == 0)
{
- if (currentSepLength == 1
- || String.CompareOrdinal(this, i, separator, 0, currentSepLength) == 0)
- {
- sepList[foundCount] = i;
- lengthList[foundCount] = currentSepLength;
- foundCount++;
- i += currentSepLength - 1;
- break;
- }
+ sepListBuilder.Append(i);
+ lengthListBuilder.Append(currentSepLength);
+ i += currentSepLength - 1;
+ break;
}
}
}
}
- return foundCount;
}
// Returns a substring of this string.
//
- public String Substring(int startIndex)
- {
- return this.Substring(startIndex, Length - startIndex);
- }
+ public string Substring(int startIndex) => Substring(startIndex, Length - startIndex);
- // Returns a substring of this string.
- //
- public String Substring(int startIndex, int length)
+ public string Substring(int startIndex, int length)
{
- //Bounds Checking.
if (startIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
@@ -1614,7 +1615,7 @@ namespace System
if (length == 0)
{
- return String.Empty;
+ return string.Empty;
}
if (startIndex == 0 && length == this.Length)
@@ -1627,7 +1628,10 @@ namespace System
private unsafe string InternalSubString(int startIndex, int length)
{
- String result = FastAllocateString(length);
+ Debug.Assert(startIndex >= 0 && startIndex <= this.Length, "StartIndex is out of range!");
+ Debug.Assert(length >= 0 && startIndex <= this.Length - length, "length is out of range!");
+
+ string result = FastAllocateString(length);
fixed (char* dest = &result._firstChar)
fixed (char* src = &_firstChar)
@@ -1639,13 +1643,13 @@ namespace System
}
// Creates a copy of this string in lower case. The culture is set by culture.
- public String ToLower()
+ public string ToLower()
{
return CultureInfo.CurrentCulture.TextInfo.ToLower(this);
}
// Creates a copy of this string in lower case. The culture is set by culture.
- public String ToLower(CultureInfo culture)
+ public string ToLower(CultureInfo culture)
{
if (culture == null)
{
@@ -1655,18 +1659,18 @@ namespace System
}
// Creates a copy of this string in lower case based on invariant culture.
- public String ToLowerInvariant()
+ public string ToLowerInvariant()
{
return CultureInfo.InvariantCulture.TextInfo.ToLower(this);
}
- public String ToUpper()
+ public string ToUpper()
{
return CultureInfo.CurrentCulture.TextInfo.ToUpper(this);
}
// Creates a copy of this string in upper case. The culture is set by culture.
- public String ToUpper(CultureInfo culture)
+ public string ToUpper(CultureInfo culture)
{
if (culture == null)
{
@@ -1676,7 +1680,7 @@ namespace System
}
//Creates a copy of this string in upper case based on invariant culture.
- public String ToUpperInvariant()
+ public string ToUpperInvariant()
{
return CultureInfo.InvariantCulture.TextInfo.ToUpper(this);
}
diff --git a/src/System.Private.CoreLib/shared/System/String.Searching.cs b/src/System.Private.CoreLib/shared/System/String.Searching.cs
index 5aa002b72..c86d13524 100644
--- a/src/System.Private.CoreLib/shared/System/String.Searching.cs
+++ b/src/System.Private.CoreLib/shared/System/String.Searching.cs
@@ -3,7 +3,10 @@
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Numerics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
namespace System
{
@@ -53,34 +56,45 @@ namespace System
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.None);
+ return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.Ordinal);
+ return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.Ordinal);
case StringComparison.OrdinalIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.OrdinalIgnoreCase);
-
+ return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.OrdinalIgnoreCase);
+
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
}
}
-
+
public unsafe int IndexOf(char value, int startIndex, int count)
{
- if (startIndex < 0 || startIndex > Length)
+ if ((uint)startIndex > (uint)Length)
throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
- if (count < 0 || count > Length - startIndex)
+ if ((uint)count > (uint)(Length - startIndex))
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
fixed (char* pChars = &_firstChar)
{
char* pCh = pChars + startIndex;
+ char* pEndCh = pCh + count;
+ if (Vector.IsHardwareAccelerated && count >= Vector<ushort>.Count * 2)
+ {
+ unchecked
+ {
+ const int elementsPerByte = sizeof(ushort) / sizeof(byte);
+ int unaligned = ((int)pCh & (Vector<byte>.Count - 1)) / elementsPerByte;
+ count = ((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1));
+ }
+ }
+ SequentialScan:
while (count >= 4)
{
if (*pCh == value) goto ReturnIndex;
@@ -101,6 +115,34 @@ namespace System
pCh++;
}
+ if (pCh < pEndCh)
+ {
+ count = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
+ // Get comparison Vector
+ Vector<ushort> vComparison = new Vector<ushort>(value);
+ while (count > 0)
+ {
+ var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(pCh));
+ if (Vector<ushort>.Zero.Equals(vMatches))
+ {
+ pCh += Vector<ushort>.Count;
+ count -= Vector<ushort>.Count;
+ continue;
+ }
+ // Find offset of first match
+ return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
+ }
+
+ if (pCh < pEndCh)
+ {
+ unchecked
+ {
+ count = (int)(pEndCh - pCh);
+ }
+ goto SequentialScan;
+ }
+ }
+
return -1;
ReturnIndex3: pCh++;
@@ -111,6 +153,43 @@ namespace System
}
}
+ // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateFirstFoundChar(Vector<ushort> match)
+ {
+ var vector64 = Vector.AsVectorUInt64(match);
+ ulong candidate = 0;
+ int i = 0;
+ // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
+ for (; i < Vector<ulong>.Count; i++)
+ {
+ candidate = vector64[i];
+ if (candidate != 0)
+ {
+ break;
+ }
+ }
+
+ // Single LEA instruction with jitted const (using function result)
+ return i * 4 + LocateFirstFoundChar(candidate);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateFirstFoundChar(ulong match)
+ {
+ unchecked
+ {
+ // Flag least significant power of two bit
+ var powerOfTwoFlag = match ^ (match - 1);
+ // Shift all powers of two into the high byte and extract
+ return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49);
+ }
+ }
+
+ private const ulong XorPowerOfTwoToHighChar = (0x03ul |
+ 0x02ul << 16 |
+ 0x01ul << 32) + 1;
+
// Returns the index of the first occurrence of any specified character in the current instance.
// The search starts at startIndex and runs to startIndex + count - 1.
//
@@ -305,17 +384,17 @@ namespace System
charMap[value & PROBABILISTICMAP_BLOCK_INDEX_MASK] |= 1u << (value >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
}
- public int IndexOf(String value)
+ public int IndexOf(string value)
{
return IndexOf(value, StringComparison.CurrentCulture);
}
- public int IndexOf(String value, int startIndex)
+ public int IndexOf(string value, int startIndex)
{
return IndexOf(value, startIndex, StringComparison.CurrentCulture);
}
- public int IndexOf(String value, int startIndex, int count)
+ public int IndexOf(string value, int startIndex, int count)
{
if (startIndex < 0 || startIndex > this.Length)
{
@@ -330,17 +409,17 @@ namespace System
return IndexOf(value, startIndex, count, StringComparison.CurrentCulture);
}
- public int IndexOf(String value, StringComparison comparisonType)
+ public int IndexOf(string value, StringComparison comparisonType)
{
return IndexOf(value, 0, this.Length, comparisonType);
}
- public int IndexOf(String value, int startIndex, StringComparison comparisonType)
+ public int IndexOf(string value, int startIndex, StringComparison comparisonType)
{
return IndexOf(value, startIndex, this.Length - startIndex, comparisonType);
}
- public int IndexOf(String value, int startIndex, int count, StringComparison comparisonType)
+ public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType)
{
// Validate inputs
if (value == null)
@@ -361,16 +440,16 @@ namespace System
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
+ return CompareInfo.Invariant.IndexOf(this, value, startIndex, count, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
- return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.Ordinal);
+ return CompareInfo.Invariant.IndexOfOrdinal(this, value, startIndex, count, ignoreCase: false);
case StringComparison.OrdinalIgnoreCase:
- return TextInfo.IndexOfStringOrdinalIgnoreCase(this, value, startIndex, count);
+ return CompareInfo.Invariant.IndexOfOrdinal(this, value, startIndex, count, ignoreCase: true);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
@@ -397,17 +476,27 @@ namespace System
if (Length == 0)
return -1;
- if (startIndex < 0 || startIndex >= Length)
+ if ((uint)startIndex >= (uint)Length)
throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
- if (count < 0 || count - 1 > startIndex)
+ if ((uint)count > (uint)startIndex + 1)
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
fixed (char* pChars = &_firstChar)
{
char* pCh = pChars + startIndex;
+ char* pEndCh = pCh - count;
//We search [startIndex..EndIndex]
+ if (Vector.IsHardwareAccelerated && count >= Vector<ushort>.Count * 2)
+ {
+ unchecked
+ {
+ const int elementsPerByte = sizeof(ushort) / sizeof(byte);
+ count = (((int)pCh & (Vector<byte>.Count - 1)) / elementsPerByte) + 1;
+ }
+ }
+ SequentialScan:
while (count >= 4)
{
if (*pCh == value) goto ReturnIndex;
@@ -428,6 +517,35 @@ namespace System
pCh--;
}
+ if (pCh > pEndCh)
+ {
+ count = (int)((pCh - pEndCh) & ~(Vector<ushort>.Count - 1));
+
+ // Get comparison Vector
+ Vector<ushort> vComparison = new Vector<ushort>(value);
+ while (count > 0)
+ {
+ char* pStart = pCh - Vector<ushort>.Count + 1;
+ var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(pStart));
+ if (Vector<ushort>.Zero.Equals(vMatches))
+ {
+ pCh -= Vector<ushort>.Count;
+ count -= Vector<ushort>.Count;
+ continue;
+ }
+ // Find offset of last match
+ return (int)(pStart - pChars) + LocateLastFoundChar(vMatches);
+ }
+
+ if (pCh > pEndCh)
+ {
+ unchecked
+ {
+ count = (int)(pCh - pEndCh);
+ }
+ goto SequentialScan;
+ }
+ }
return -1;
ReturnIndex3: pCh--;
@@ -438,6 +556,40 @@ namespace System
}
}
+ // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateLastFoundChar(Vector<ushort> match)
+ {
+ var vector64 = Vector.AsVectorUInt64(match);
+ ulong candidate = 0;
+ int i = Vector<ulong>.Count - 1;
+ // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
+ for (; i >= 0; i--)
+ {
+ candidate = vector64[i];
+ if (candidate != 0)
+ {
+ break;
+ }
+ }
+
+ // Single LEA instruction with jitted const (using function result)
+ return i * 4 + LocateLastFoundChar(candidate);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LocateLastFoundChar(ulong match)
+ {
+ // Find the most significant char that has its highest bit set
+ int index = 3;
+ while ((long)match > 0)
+ {
+ match = match << 16;
+ index--;
+ }
+ return index;
+ }
+
// Returns the index of the last occurrence of any specified character in the current instance.
// The search starts at startIndex and runs backwards to startIndex - count + 1.
// The character at position startIndex is included in the search. startIndex is the larger
@@ -521,17 +673,17 @@ namespace System
// The character at position startIndex is included in the search. startIndex is the larger
// index within the string.
//
- public int LastIndexOf(String value)
+ public int LastIndexOf(string value)
{
return LastIndexOf(value, this.Length - 1, this.Length, StringComparison.CurrentCulture);
}
- public int LastIndexOf(String value, int startIndex)
+ public int LastIndexOf(string value, int startIndex)
{
return LastIndexOf(value, startIndex, startIndex + 1, StringComparison.CurrentCulture);
}
- public int LastIndexOf(String value, int startIndex, int count)
+ public int LastIndexOf(string value, int startIndex, int count)
{
if (count < 0)
{
@@ -541,17 +693,17 @@ namespace System
return LastIndexOf(value, startIndex, count, StringComparison.CurrentCulture);
}
- public int LastIndexOf(String value, StringComparison comparisonType)
+ public int LastIndexOf(string value, StringComparison comparisonType)
{
return LastIndexOf(value, this.Length - 1, this.Length, comparisonType);
}
- public int LastIndexOf(String value, int startIndex, StringComparison comparisonType)
+ public int LastIndexOf(string value, int startIndex, StringComparison comparisonType)
{
return LastIndexOf(value, startIndex, startIndex + 1, comparisonType);
}
- public int LastIndexOf(String value, int startIndex, int count, StringComparison comparisonType)
+ public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
@@ -589,16 +741,16 @@ namespace System
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
+ return CompareInfo.Invariant.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
- return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.Ordinal);
+ return CompareInfo.Invariant.LastIndexOfOrdinal(this, value, startIndex, count, ignoreCase: false);
case StringComparison.OrdinalIgnoreCase:
- return TextInfo.LastIndexOfStringOrdinalIgnoreCase(this, value, startIndex, count);
+ return CompareInfo.Invariant.LastIndexOfOrdinal(this, value, startIndex, count, ignoreCase: true);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
diff --git a/src/System.Private.CoreLib/shared/System/StringComparer.cs b/src/System.Private.CoreLib/shared/System/StringComparer.cs
index 73c013599..de311e91d 100644
--- a/src/System.Private.CoreLib/shared/System/StringComparer.cs
+++ b/src/System.Private.CoreLib/shared/System/StringComparer.cs
@@ -13,8 +13,8 @@ namespace System
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>
{
- private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);
- private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);
+ private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.None);
+ private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase);
private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer();
private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer();
@@ -38,7 +38,7 @@ namespace System
{
get
{
- return new CultureAwareComparer(CultureInfo.CurrentCulture, false);
+ return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.None);
}
}
@@ -46,7 +46,7 @@ namespace System
{
get
{
- return new CultureAwareComparer(CultureInfo.CurrentCulture, true);
+ return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
}
}
@@ -94,8 +94,18 @@ namespace System
{
throw new ArgumentNullException(nameof(culture));
}
+
+ return new CultureAwareComparer(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
+ }
+
+ public static StringComparer Create(CultureInfo culture, CompareOptions options)
+ {
+ if (culture == null)
+ {
+ throw new ArgumentException(nameof(culture));
+ }
- return new CultureAwareComparer(culture, ignoreCase);
+ return new CultureAwareComparer(culture, options);
}
public int Compare(object x, object y)
@@ -123,7 +133,6 @@ namespace System
throw new ArgumentException(SR.Argument_ImplementIComparable);
}
-
public new bool Equals(Object x, Object y)
{
if (x == y) return true;
@@ -163,32 +172,52 @@ namespace System
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public sealed class CultureAwareComparer : StringComparer
+ public sealed class CultureAwareComparer : StringComparer, ISerializable
{
+ private const CompareOptions ValidCompareMaskOffFlags = ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);
+
private readonly CompareInfo _compareInfo; // Do not rename (binary serialization)
- private readonly bool _ignoreCase; // Do not rename (binary serialization)
+ private CompareOptions _options;
- internal CultureAwareComparer(CultureInfo culture, bool ignoreCase)
+ internal CultureAwareComparer(CultureInfo culture, CompareOptions options) : this(culture.CompareInfo, options) { }
+
+ internal CultureAwareComparer(CompareInfo compareInfo, CompareOptions options)
{
- _compareInfo = culture.CompareInfo;
- _ignoreCase = ignoreCase;
+ _compareInfo = compareInfo;
+
+ if ((options & ValidCompareMaskOffFlags) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
+ }
+ _options = options;
}
- private CompareOptions Options => _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
+ private CultureAwareComparer(SerializationInfo info, StreamingContext context)
+ {
+ _compareInfo = (CompareInfo)info.GetValue("_compareInfo", typeof(CompareInfo));
+ bool ignoreCase = info.GetBoolean("_ignoreCase");
+
+ var obj = info.GetValueNoThrow("_options", typeof(CompareOptions));
+ if (obj != null)
+ _options = (CompareOptions)obj;
+
+ // fix up the _options value in case we are getting old serialized object not having _options
+ _options |= ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
+ }
public override int Compare(string x, string y)
{
if (object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
- return _compareInfo.Compare(x, y, Options);
+ return _compareInfo.Compare(x, y, _options);
}
public override bool Equals(string x, string y)
{
if (object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
- return _compareInfo.Compare(x, y, Options) == 0;
+ return _compareInfo.Compare(x, y, _options) == 0;
}
public override int GetHashCode(string obj)
@@ -197,7 +226,7 @@ namespace System
{
throw new ArgumentNullException(nameof(obj));
}
- return _compareInfo.GetHashCodeOfString(obj, Options);
+ return _compareInfo.GetHashCodeOfString(obj, _options);
}
// Equals method for the comparer itself.
@@ -206,14 +235,20 @@ namespace System
CultureAwareComparer comparer = obj as CultureAwareComparer;
return
comparer != null &&
- _ignoreCase == comparer._ignoreCase &&
+ _options == comparer._options &&
_compareInfo.Equals(comparer._compareInfo);
}
public override int GetHashCode()
{
- int hashCode = _compareInfo.GetHashCode();
- return _ignoreCase ? ~hashCode : hashCode;
+ return _compareInfo.GetHashCode() ^ ((int)_options & 0x7FFFFFFF);
+ }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("_compareInfo", _compareInfo);
+ info.AddValue("_options", _options);
+ info.AddValue("_ignoreCase", (_options & CompareOptions.IgnoreCase) != 0);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs b/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs
index 1c127b19d..2d6152de5 100644
--- a/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs
+++ b/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs
@@ -17,7 +17,7 @@ namespace System
throw new ArgumentOutOfRangeException(nameof(comparisonType));
public static bool Equals(this ReadOnlySpan<char> left, string right) =>
- Equals(left, right.AsReadOnlySpan());
+ Equals(left, right.AsSpan());
public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right)
{
@@ -33,7 +33,7 @@ namespace System
return false;
}
}
-
+
return true;
}
@@ -57,23 +57,6 @@ namespace System
return true;
}
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> source)
- {
- int startIndex = 0, endIndex = source.Length - 1;
-
- while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex]))
- {
- startIndex++;
- }
-
- while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex]))
- {
- endIndex--;
- }
-
- return source.Slice(startIndex, endIndex - startIndex + 1);
- }
-
public static int IndexOf(this ReadOnlySpan<char> source, char value) =>
IndexOf(source, value, 0);
@@ -105,9 +88,12 @@ namespace System
public static ReadOnlySpan<char> Remove(this ReadOnlySpan<char> source, int startIndex, int count)
{
- if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
- if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
- if (count > source.Length - startIndex) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount);
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
+ if (count > source.Length - startIndex)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount);
if (count == 0)
{
@@ -125,5 +111,29 @@ namespace System
source.Slice(startIndex + count).CopyTo(result.Slice(startIndex));
return result;
}
+
+ // Returns the index of the last occurrence of a specified character in the current instance.
+ public static int LastIndexOf(this ReadOnlySpan<char> source, char value)
+ {
+ if (source.Length == 0)
+ return -1;
+
+ for (int i = source.Length - 1; i >= 0; i--)
+ {
+ if (source[i] == value)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public static void CheckStringComparison(StringComparison comparisonType)
+ {
+ // Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase]
+ if ((uint)(comparisonType - StringComparison.CurrentCulture) > (StringComparison.OrdinalIgnoreCase - StringComparison.CurrentCulture))
+ {
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
+ }
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs
index d9da9377d..a7804c399 100644
--- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs
@@ -423,53 +423,12 @@ namespace System.Text
}
AssertInvariants();
-
- StringBuilder chunk = this;
- int sourceEndIndex = startIndex + length;
-
string result = string.FastAllocateString(length);
- int curDestIndex = length;
unsafe
{
fixed (char* destinationPtr = result)
{
- while (curDestIndex > 0)
- {
- int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset;
- if (chunkEndIndex >= 0)
- {
- chunkEndIndex = Math.Min(chunkEndIndex, chunk.m_ChunkLength);
-
- int countLeft = curDestIndex;
- int chunkCount = countLeft;
- int chunkStartIndex = chunkEndIndex - countLeft;
- if (chunkStartIndex < 0)
- {
- chunkCount += chunkStartIndex;
- chunkStartIndex = 0;
- }
- curDestIndex -= chunkCount;
-
- if (chunkCount > 0)
- {
- // Work off of local variables so that they are stable even in the presence of race conditions
- char[] sourceArray = chunk.m_ChunkChars;
-
- // Check that we will not overrun our boundaries.
- if ((uint)(chunkCount + curDestIndex) <= (uint)length && (uint)(chunkCount + chunkStartIndex) <= (uint)sourceArray.Length)
- {
- fixed (char* sourcePtr = &sourceArray[chunkStartIndex])
- string.wstrcpy(destinationPtr + curDestIndex, sourcePtr, chunkCount);
- }
- else
- {
- throw new ArgumentOutOfRangeException(nameof(chunkCount), SR.ArgumentOutOfRange_Index);
- }
- }
- }
- chunk = chunk.m_ChunkPrevious;
- }
-
+ this.CopyTo(startIndex, new Span<char>(destinationPtr, length), length);
return result;
}
}
@@ -790,6 +749,79 @@ namespace System.Text
}
}
+ public StringBuilder Append(StringBuilder value)
+ {
+ if (value != null && value.Length != 0)
+ {
+ return AppendCore(value, 0, value.Length);
+ }
+ return this;
+ }
+
+ public StringBuilder Append(StringBuilder value, int startIndex, int count)
+ {
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GenericPositive);
+ }
+
+ if (value == null)
+ {
+ if (startIndex == 0 && count == 0)
+ {
+ return this;
+ }
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ if (count == 0)
+ {
+ return this;
+ }
+
+ if (count > value.Length - startIndex)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ return AppendCore(value, startIndex, count);
+ }
+
+ private StringBuilder AppendCore(StringBuilder value, int startIndex, int count)
+ {
+ if (value == this)
+ return Append(value.ToString(startIndex, count));
+
+ int newLength = Length + count;
+
+ if ((uint)newLength > (uint)m_MaxCapacity)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Capacity), SR.ArgumentOutOfRange_Capacity);
+ }
+
+ while (count > 0)
+ {
+ int length = Math.Min(m_ChunkChars.Length - m_ChunkLength, count);
+ if (length == 0)
+ {
+ ExpandByABlock(count);
+ length = Math.Min(m_ChunkChars.Length - m_ChunkLength, count);
+ }
+ value.CopyTo(startIndex, new Span<char>(m_ChunkChars, m_ChunkLength, length), length);
+
+ m_ChunkLength += length;
+ startIndex += length;
+ count -= length;
+ }
+
+ return this;
+ }
+
public StringBuilder AppendLine() => Append(Environment.NewLine);
public StringBuilder AppendLine(string value)
@@ -1520,7 +1552,7 @@ namespace System.Text
if (startPos != pos)
{
// There was no brace escaping, extract the item format as a single string
- itemFormatSpan = format.AsReadOnlySpan().Slice(startPos, pos - startPos);
+ itemFormatSpan = format.AsSpan().Slice(startPos, pos - startPos);
}
}
else
@@ -1646,6 +1678,35 @@ namespace System.Text
}
/// <summary>
+ /// Determines if the contents of this builder are equal to the contents of ReadOnlySpan<char>.
+ /// </summary>
+ /// <param name="value">The ReadOnlySpan{char}.</param>
+ public bool Equals(ReadOnlySpan<char> value)
+ {
+ if (value.Length != Length)
+ return false;
+
+ StringBuilder sbChunk = this;
+ int offset = 0;
+
+ do
+ {
+ int chunk_length = sbChunk.m_ChunkLength;
+ offset += chunk_length;
+
+ ReadOnlySpan<char> chunk = new ReadOnlySpan<char>(sbChunk.m_ChunkChars, 0, chunk_length);
+
+ if (!chunk.Equals(value.Slice(value.Length - offset, chunk_length)))
+ return false;
+
+ sbChunk = sbChunk.m_ChunkPrevious;
+ } while (sbChunk != null);
+
+ Debug.Assert(offset == Length);
+ return true;
+ }
+
+ /// <summary>
/// Replaces all instances of one string with another in part of this builder.
/// </summary>
/// <param name="oldValue">The string to replace.</param>
diff --git a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
index a89af8edb..67f87c9b0 100644
--- a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
@@ -852,7 +852,7 @@ namespace System.Text
{
if (ch == 0)
{
- // Check if there's anthing left to get out of the fallback buffer
+ // Check if there's anything left to get out of the fallback buffer
ch = fallbackBuffer != null ? fallbackBuffer.InternalGetNextChar() : 0;
if (ch > 0)
{
diff --git a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs
index 0a91eb0f6..18d564867 100644
--- a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs
@@ -5,6 +5,7 @@
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace System.Text
{
@@ -21,27 +22,56 @@ namespace System.Text
_pos = 0;
}
- public int Length => _pos;
+ public int Length
+ {
+ get => _pos;
+ set
+ {
+ Debug.Assert(value <= _chars.Length);
+ _pos = value;
+ }
+ }
+
+ public int Capacity => _chars.Length;
+
+ public void EnsureCapacity(int capacity)
+ {
+ if (capacity > _chars.Length)
+ Grow(capacity - _chars.Length);
+ }
+
+ public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars);
+
+ public ref char this[int index]
+ {
+ get
+ {
+ Debug.Assert(index < _pos);
+ return ref _chars[index];
+ }
+ }
public override string ToString()
{
var s = new string(_chars.Slice(0, _pos));
- Clear();
+ Dispose();
return s;
}
+ public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
+
public bool TryCopyTo(Span<char> destination, out int charsWritten)
{
if (_chars.Slice(0, _pos).TryCopyTo(destination))
{
charsWritten = _pos;
- Clear();
+ Dispose();
return true;
}
else
{
charsWritten = 0;
- Clear();
+ Dispose();
return false;
}
}
@@ -97,8 +127,7 @@ namespace System.Text
Grow(s.Length);
}
- bool copied = s.AsReadOnlySpan().TryCopyTo(_chars.Slice(pos));
- Debug.Assert(copied, "Grow should have made enough room to successfully copy");
+ s.AsSpan().CopyTo(_chars.Slice(pos));
_pos += s.Length;
}
@@ -133,6 +162,18 @@ namespace System.Text
_pos += length;
}
+ public unsafe void Append(ReadOnlySpan<char> value)
+ {
+ int pos = _pos;
+ if (pos > _chars.Length - value.Length)
+ {
+ Grow(value.Length);
+ }
+
+ value.CopyTo(_chars.Slice(_pos));
+ _pos += value.Length;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<char> AppendSpan(int length)
{
@@ -156,12 +197,11 @@ namespace System.Text
[MethodImpl(MethodImplOptions.NoInlining)]
private void Grow(int requiredAdditionalCapacity)
{
- Debug.Assert(requiredAdditionalCapacity > _chars.Length - _pos);
+ Debug.Assert(requiredAdditionalCapacity > 0);
char[] poolArray = ArrayPool<char>.Shared.Rent(Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2));
- bool success = _chars.TryCopyTo(poolArray);
- Debug.Assert(success);
+ _chars.CopyTo(poolArray);
char[] toReturn = _arrayToReturnToPool;
_chars = _arrayToReturnToPool = poolArray;
@@ -172,7 +212,7 @@ namespace System.Text
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void Clear()
+ public void Dispose()
{
char[] toReturn = _arrayToReturnToPool;
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
diff --git a/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs b/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
index 59c8fb3c8..3531265be 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
@@ -125,7 +125,7 @@ namespace System.Threading
public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap();
// Instance without any key/value pairs. Used as a singleton/
- private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap
+ internal sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap
{
public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
{
@@ -144,7 +144,7 @@ namespace System.Threading
}
// Instance with one key/value pair.
- private sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap
+ internal sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap
{
private readonly IAsyncLocal _key1;
private readonly object _value1;
diff --git a/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs b/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
index 2d5f5be19..9f27c6dcb 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
@@ -21,40 +21,18 @@ namespace System.Threading
{
public delegate void ContextCallback(Object state);
- internal struct ExecutionContextSwitcher
- {
- internal ExecutionContext m_ec;
- internal SynchronizationContext m_sc;
-
- internal void Undo(Thread currentThread)
- {
- Debug.Assert(currentThread == Thread.CurrentThread);
-
- // The common case is that these have not changed, so avoid the cost of a write if not needed.
- if (currentThread.SynchronizationContext != m_sc)
- {
- currentThread.SynchronizationContext = m_sc;
- }
-
- if (currentThread.ExecutionContext != m_ec)
- {
- ExecutionContext.Restore(currentThread, m_ec);
- }
- }
- }
-
public sealed class ExecutionContext : IDisposable, ISerializable
{
- internal static readonly ExecutionContext Default = new ExecutionContext();
+ internal static readonly ExecutionContext Default = new ExecutionContext(isDefault: true);
private readonly IAsyncLocalValueMap m_localValues;
private readonly IAsyncLocal[] m_localChangeNotifications;
private readonly bool m_isFlowSuppressed;
+ private readonly bool m_isDefault;
- private ExecutionContext()
+ private ExecutionContext(bool isDefault)
{
- m_localValues = AsyncLocalValueMap.Empty;
- m_localChangeNotifications = Array.Empty<IAsyncLocal>();
+ m_isDefault = isDefault;
}
private ExecutionContext(
@@ -86,12 +64,14 @@ namespace System.Threading
Debug.Assert(isFlowSuppressed != m_isFlowSuppressed);
if (!isFlowSuppressed &&
- m_localValues == Default.m_localValues &&
- m_localChangeNotifications == Default.m_localChangeNotifications)
+ (m_localValues == null ||
+ m_localValues.GetType() == typeof(AsyncLocalValueMap.EmptyAsyncLocalValueMap))
+ )
{
return null; // implies the default context
}
- return new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed);
+ // Flow suppressing a Default context will have null values, set them to Empty
+ return new ExecutionContext(m_localValues ?? AsyncLocalValueMap.Empty, m_localChangeNotifications ?? Array.Empty<IAsyncLocal>(), isFlowSuppressed);
}
public static AsyncFlowControl SuppressFlow()
@@ -128,134 +108,248 @@ namespace System.Threading
return executionContext != null && executionContext.m_isFlowSuppressed;
}
+ internal bool HasChangeNotifications => m_localChangeNotifications != null;
+
+ internal bool IsDefault => m_isDefault;
+
public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state)
{
+ // Note: ExecutionContext.Run is an extremely hot function and used by every await, ThreadPool execution, etc.
if (executionContext == null)
- throw new InvalidOperationException(SR.InvalidOperation_NullContext);
+ {
+ ThrowNullContext();
+ }
- Thread currentThread = Thread.CurrentThread;
- ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher);
+ RunInternal(executionContext, callback, state);
+ }
+
+ internal static void RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
+ {
+ // Note: ExecutionContext.RunInternal is an extremely hot function and used by every await, ThreadPool execution, etc.
+ // Note: Manual enregistering may be addressed by "Exception Handling Write Through Optimization"
+ // https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/eh-writethru.md
+
+ // Enregister variables with 0 post-fix so they can be used in registers without EH forcing them to stack
+ // Capture references to Thread Contexts
+ Thread currentThread0 = Thread.CurrentThread;
+ Thread currentThread = currentThread0;
+ ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
+
+ // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+ // This allows us to restore them and undo any Context changes made in callback.Invoke
+ // so that they won't "leak" back into caller.
+ // These variables will cross EH so be forced to stack
+ ExecutionContext previousExecutionCtx = previousExecutionCtx0;
+ SynchronizationContext previousSyncCtx = currentThread0.SynchronizationContext;
+
+ if (executionContext != null && executionContext.m_isDefault)
+ {
+ // Default is a null ExecutionContext internally
+ executionContext = null;
+ }
+
+ if (previousExecutionCtx0 != executionContext)
+ {
+ // Restore changed ExecutionContext
+ currentThread0.ExecutionContext = executionContext;
+ if ((executionContext != null && executionContext.HasChangeNotifications) ||
+ (previousExecutionCtx0 != null && previousExecutionCtx0.HasChangeNotifications))
+ {
+ // There are change notifications; trigger any affected
+ OnValuesChanged(previousExecutionCtx0, executionContext);
+ }
+ }
+
+ ExceptionDispatchInfo edi = null;
try
{
- EstablishCopyOnWriteScope(currentThread, ref ecsw);
- ExecutionContext.Restore(currentThread, executionContext);
- callback(state);
+ callback.Invoke(state);
}
- catch
+ catch (Exception ex)
{
// Note: we have a "catch" rather than a "finally" because we want
// to stop the first pass of EH here. That way we can restore the previous
- // context before any of our callers' EH filters run. That means we need to
- // end the scope separately in the non-exceptional case below.
- ecsw.Undo(currentThread);
- throw;
+ // context before any of our callers' EH filters run.
+ edi = ExceptionDispatchInfo.Capture(ex);
}
- ecsw.Undo(currentThread);
- }
- internal static void Restore(Thread currentThread, ExecutionContext executionContext)
- {
- Debug.Assert(currentThread == Thread.CurrentThread);
-
- ExecutionContext previous = currentThread.ExecutionContext ?? Default;
- currentThread.ExecutionContext = executionContext;
-
- // New EC could be null if that's what ECS.Undo saved off.
- // For the purposes of dealing with context change, treat this as the default EC
- executionContext = executionContext ?? Default;
-
- if (previous != executionContext)
+ // Re-enregistrer variables post EH with 1 post-fix so they can be used in registers rather than from stack
+ SynchronizationContext previousSyncCtx1 = previousSyncCtx;
+ Thread currentThread1 = currentThread;
+ // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+ if (currentThread1.SynchronizationContext != previousSyncCtx1)
{
- OnContextChanged(previous, executionContext);
+ // Restore changed SynchronizationContext back to previous
+ currentThread1.SynchronizationContext = previousSyncCtx1;
}
- }
- internal static void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw)
- {
- Debug.Assert(currentThread == Thread.CurrentThread);
+ ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
+ ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
+ if (currentExecutionCtx1 != previousExecutionCtx1)
+ {
+ // Restore changed ExecutionContext back to previous
+ currentThread1.ExecutionContext = previousExecutionCtx1;
+ if ((currentExecutionCtx1 != null && currentExecutionCtx1.HasChangeNotifications) ||
+ (previousExecutionCtx1 != null && previousExecutionCtx1.HasChangeNotifications))
+ {
+ // There are change notifications; trigger any affected
+ OnValuesChanged(currentExecutionCtx1, previousExecutionCtx1);
+ }
+ }
- ecsw.m_ec = currentThread.ExecutionContext;
- ecsw.m_sc = currentThread.SynchronizationContext;
+ // If exception was thrown by callback, rethrow it now original contexts are restored
+ edi?.Throw();
}
- private static void OnContextChanged(ExecutionContext previous, ExecutionContext current)
+ internal static void OnValuesChanged(ExecutionContext previousExecutionCtx, ExecutionContext nextExecutionCtx)
{
- Debug.Assert(previous != null);
- Debug.Assert(current != null);
- Debug.Assert(previous != current);
+ Debug.Assert(previousExecutionCtx != nextExecutionCtx);
- foreach (IAsyncLocal local in previous.m_localChangeNotifications)
- {
- object previousValue;
- object currentValue;
- previous.m_localValues.TryGetValue(local, out previousValue);
- current.m_localValues.TryGetValue(local, out currentValue);
+ // Collect Change Notifications
+ IAsyncLocal[] previousChangeNotifications = previousExecutionCtx?.m_localChangeNotifications;
+ IAsyncLocal[] nextChangeNotifications = nextExecutionCtx?.m_localChangeNotifications;
- if (previousValue != currentValue)
- local.OnValueChanged(previousValue, currentValue, true);
- }
+ // At least one side must have notifications
+ Debug.Assert(previousChangeNotifications != null || nextChangeNotifications != null);
- if (current.m_localChangeNotifications != previous.m_localChangeNotifications)
+ // Fire Change Notifications
+ try
{
- try
+ if (previousChangeNotifications != null && nextChangeNotifications != null)
{
- foreach (IAsyncLocal local in current.m_localChangeNotifications)
+ // Notifications can't exist without values
+ Debug.Assert(previousExecutionCtx.m_localValues != null);
+ Debug.Assert(nextExecutionCtx.m_localValues != null);
+ // Both contexts have change notifications, check previousExecutionCtx first
+ foreach (IAsyncLocal local in previousChangeNotifications)
{
- // If the local has a value in the previous context, we already fired the event for that local
- // in the code above.
- object previousValue;
- if (!previous.m_localValues.TryGetValue(local, out previousValue))
+ previousExecutionCtx.m_localValues.TryGetValue(local, out object previousValue);
+ nextExecutionCtx.m_localValues.TryGetValue(local, out object currentValue);
+
+ if (previousValue != currentValue)
{
- object currentValue;
- current.m_localValues.TryGetValue(local, out currentValue);
+ local.OnValueChanged(previousValue, currentValue, contextChanged: true);
+ }
+ }
- if (previousValue != currentValue)
- local.OnValueChanged(previousValue, currentValue, true);
+ if (nextChangeNotifications != previousChangeNotifications)
+ {
+ // Check for additional notifications in nextExecutionCtx
+ foreach (IAsyncLocal local in nextChangeNotifications)
+ {
+ // If the local has a value in the previous context, we already fired the event
+ // for that local in the code above.
+ if (!previousExecutionCtx.m_localValues.TryGetValue(local, out object previousValue))
+ {
+ nextExecutionCtx.m_localValues.TryGetValue(local, out object currentValue);
+ if (previousValue != currentValue)
+ {
+ local.OnValueChanged(previousValue, currentValue, contextChanged: true);
+ }
+ }
}
}
}
- catch (Exception ex)
+ else if (previousChangeNotifications != null)
{
- Environment.FailFast(
- SR.ExecutionContext_ExceptionInAsyncLocalNotification,
- ex);
+ // Notifications can't exist without values
+ Debug.Assert(previousExecutionCtx.m_localValues != null);
+ // No current values, so just check previous against null
+ foreach (IAsyncLocal local in previousChangeNotifications)
+ {
+ previousExecutionCtx.m_localValues.TryGetValue(local, out object previousValue);
+ if (previousValue != null)
+ {
+ local.OnValueChanged(previousValue, null, contextChanged: true);
+ }
+ }
}
+ else // Implied: nextChangeNotifications != null
+ {
+ // Notifications can't exist without values
+ Debug.Assert(nextExecutionCtx.m_localValues != null);
+ // No previous values, so just check current against null
+ foreach (IAsyncLocal local in nextChangeNotifications)
+ {
+ nextExecutionCtx.m_localValues.TryGetValue(local, out object currentValue);
+ if (currentValue != null)
+ {
+ local.OnValueChanged(null, currentValue, contextChanged: true);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Environment.FailFast(
+ SR.ExecutionContext_ExceptionInAsyncLocalNotification,
+ ex);
}
}
+ [StackTraceHidden]
+ private static void ThrowNullContext()
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_NullContext);
+ }
+
internal static object GetLocalValue(IAsyncLocal local)
{
ExecutionContext current = Thread.CurrentThread.ExecutionContext;
if (current == null)
+ {
return null;
+ }
- object value;
- current.m_localValues.TryGetValue(local, out value);
+ current.m_localValues.TryGetValue(local, out object value);
return value;
}
internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications)
{
- ExecutionContext current = Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default;
+ ExecutionContext current = Thread.CurrentThread.ExecutionContext;
- object previousValue;
- bool hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue);
+ object previousValue = null;
+ bool hadPreviousValue = false;
+ if (current != null)
+ {
+ hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue);
+ }
if (previousValue == newValue)
+ {
return;
+ }
- IAsyncLocalValueMap newValues = current.m_localValues.Set(local, newValue);
+ IAsyncLocal[] newChangeNotifications = null;
+ IAsyncLocalValueMap newValues;
+ bool isFlowSuppressed = false;
+ if (current != null)
+ {
+ isFlowSuppressed = current.m_isFlowSuppressed;
+ newValues = current.m_localValues.Set(local, newValue);
+ newChangeNotifications = current.m_localChangeNotifications;
+ }
+ else
+ {
+ // First AsyncLocal
+ newValues = new AsyncLocalValueMap.OneElementAsyncLocalValueMap(local, newValue);
+ }
//
// Either copy the change notification array, or create a new one, depending on whether we need to add a new item.
//
- IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications;
if (needChangeNotifications)
{
if (hadPreviousValue)
{
+ Debug.Assert(newChangeNotifications != null);
Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0);
}
+ else if (newChangeNotifications == null)
+ {
+ newChangeNotifications = new IAsyncLocal[1] { local };
+ }
else
{
int newNotificationIndex = newChangeNotifications.Length;
@@ -264,12 +358,14 @@ namespace System.Threading
}
}
- Thread.CurrentThread.ExecutionContext =
- new ExecutionContext(newValues, newChangeNotifications, current.m_isFlowSuppressed);
+ Thread.CurrentThread.ExecutionContext =
+ (!isFlowSuppressed && newValues.GetType() == typeof(AsyncLocalValueMap.EmptyAsyncLocalValueMap)) ?
+ null : // No values, return to Default context
+ new ExecutionContext(newValues, newChangeNotifications, isFlowSuppressed);
if (needChangeNotifications)
{
- local.OnValueChanged(previousValue, newValue, false);
+ local.OnValueChanged(previousValue, newValue, contextChanged: false);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs b/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs
index 3c4aad603..45175488e 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs
@@ -1249,7 +1249,7 @@ namespace System.Threading
}
// Don't want to Sleep(1) in this spin wait:
- // - Don't want to spin for that long, since a proper wait will follow when the spin wait fails. The artifical
+ // - Don't want to spin for that long, since a proper wait will follow when the spin wait fails. The artificial
// delay introduced by Sleep(1) will in some cases be much longer than desired.
// - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails
// anyway, so it's preferable to put the thread into the proper wait state
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/IValueTaskSource.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/IValueTaskSource.cs
new file mode 100644
index 000000000..e411146a1
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/IValueTaskSource.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.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading.Tasks.Sources
+{
+ /// <summary>
+ /// Flags passed from <see cref="ValueTask"/> and <see cref="ValueTask{TResult}"/> to
+ /// <see cref="IValueTaskSource.OnCompleted"/> and <see cref="IValueTaskSource{TResult}.OnCompleted"/>
+ /// to control behavior.
+ /// </summary>
+ [Flags]
+ public enum ValueTaskSourceOnCompletedFlags
+ {
+ /// <summary>
+ /// No requirements are placed on how the continuation is invoked.
+ /// </summary>
+ None,
+ /// <summary>
+ /// Set if OnCompleted should capture the current scheduling context (e.g. SynchronizationContext)
+ /// and use it when queueing the continuation for execution. If this is not set, the implementation
+ /// may choose to execute the continuation in an arbitrary location.
+ /// </summary>
+ UseSchedulingContext = 0x1,
+ /// <summary>
+ /// Set if OnCompleted should capture the current ExecutionContext and use it to run the continuation.
+ /// </summary>
+ FlowExecutionContext = 0x2,
+ }
+
+ /// <summary>Indicates the status of an <see cref="IValueTaskSource"/> or <see cref="IValueTaskSource{TResult}"/>.</summary>
+ public enum ValueTaskSourceStatus
+ {
+ /// <summary>The operation has not yet completed.</summary>
+ Pending = 0,
+ /// <summary>The operation completed successfully.</summary>
+ Succeeded = 1,
+ /// <summary>The operation completed with an error.</summary>
+ Faulted = 2,
+ /// <summary>The operation completed due to cancellation.</summary>
+ Canceled = 3
+ }
+
+ /// <summary>Represents an object that can be wrapped by a <see cref="ValueTask"/>.</summary>
+ public interface IValueTaskSource
+ {
+ /// <summary>Gets the status of the current operation.</summary>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ ValueTaskSourceStatus GetStatus(short token);
+
+ /// <summary>Schedules the continuation action for this <see cref="IValueTaskSource"/>.</summary>
+ /// <param name="continuation">The continuation to invoke when the operation has completed.</param>
+ /// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ /// <param name="flags">The flags describing the behavior of the continuation.</param>
+ void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags);
+
+ /// <summary>Gets the result of the <see cref="IValueTaskSource"/>.</summary>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ void GetResult(short token);
+ }
+
+ /// <summary>Represents an object that can be wrapped by a <see cref="ValueTask{TResult}"/>.</summary>
+ /// <typeparam name="TResult">Specifies the type of data returned from the object.</typeparam>
+ public interface IValueTaskSource<out TResult>
+ {
+ /// <summary>Gets the status of the current operation.</summary>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ ValueTaskSourceStatus GetStatus(short token);
+
+ /// <summary>Schedules the continuation action for this <see cref="IValueTaskSource{TResult}"/>.</summary>
+ /// <param name="continuation">The continuation to invoke when the operation has completed.</param>
+ /// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ /// <param name="flags">The flags describing the behavior of the continuation.</param>
+ void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags);
+
+ /// <summary>Gets the result of the <see cref="IValueTaskSource{TResult}"/>.</summary>
+ /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
+ TResult GetResult(short token);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
index de9b01632..18e4a27a4 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
@@ -3,72 +3,401 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
-using System.ComponentModel;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Threading.Tasks.Sources;
+
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
namespace System.Threading.Tasks
{
- /// <summary>
- /// Provides a value type that wraps a <see cref="Task{TResult}"/> and a <typeparamref name="TResult"/>,
- /// only one of which is used.
- /// </summary>
- /// <typeparam name="TResult">The type of the result.</typeparam>
+ /// <summary>Provides an awaitable result of an asynchronous operation.</summary>
+ /// <remarks>
+ /// <see cref="ValueTask"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
+ /// should be extracted using <see cref="AsTask"/>. Such operations might include caching an instance to be awaited later,
+ /// registering multiple continuations with a single operation, awaiting the same task multiple times, and using combinators over
+ /// multiple operations.
+ /// </remarks>
+ [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))]
+ [StructLayout(LayoutKind.Auto)]
+ public readonly struct ValueTask : IEquatable<ValueTask>
+ {
+ internal static Task CompletedTask
+#if netstandard
+ { get; } = Task.Delay(0);
+#else
+ => Task.CompletedTask;
+#endif
+
+ /// <summary>null if representing a successful synchronous completion, otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
+ internal readonly object _obj;
+ /// <summary>Flags providing additional details about the ValueTask's contents and behavior.</summary>
+ internal readonly ValueTaskFlags _flags;
+ /// <summary>Opaque value passed through to the <see cref="IValueTaskSource"/>.</summary>
+ internal readonly short _token;
+
+ // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation.
+
+ /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="Task"/> that represents the operation.</summary>
+ /// <param name="task">The task.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ValueTask(Task task)
+ {
+ if (task == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
+ }
+
+ _obj = task;
+
+ _flags = ValueTaskFlags.ObjectIsTask;
+ _token = 0;
+ }
+
+ /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="IValueTaskSource"/> object that represents the operation.</summary>
+ /// <param name="source">The source.</param>
+ /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ValueTask(IValueTaskSource source, short token)
+ {
+ if (source == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
+ }
+
+ _obj = source;
+ _token = token;
+
+ _flags = 0;
+ }
+
+ /// <summary>Non-verified initialization of the struct to the specified values.</summary>
+ /// <param name="obj">The object.</param>
+ /// <param name="token">The token.</param>
+ /// <param name="flags">The flags.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ValueTask(object obj, short token, ValueTaskFlags flags)
+ {
+ _obj = obj;
+ _token = token;
+ _flags = flags;
+ }
+
+ /// <summary>Gets whether the contination should be scheduled to the current context.</summary>
+ internal bool ContinueOnCapturedContext
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (_flags & ValueTaskFlags.AvoidCapturedContext) == 0;
+ }
+
+ /// <summary>Gets whether the object in the <see cref="_obj"/> field is a <see cref="Task"/>.</summary>
+ internal bool ObjectIsTask
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (_flags & ValueTaskFlags.ObjectIsTask) != 0;
+ }
+
+ /// <summary>Returns the <see cref="Task"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Task UnsafeGetTask()
+ {
+ Debug.Assert(ObjectIsTask);
+ Debug.Assert(_obj is Task);
+ return Unsafe.As<Task>(_obj);
+ }
+
+ /// <summary>Returns the <see cref="IValueTaskSource"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal IValueTaskSource UnsafeGetValueTaskSource()
+ {
+ Debug.Assert(!ObjectIsTask);
+ Debug.Assert(_obj is IValueTaskSource);
+ return Unsafe.As<IValueTaskSource>(_obj);
+ }
+
+ /// <summary>Returns the hash code for this instance.</summary>
+ public override int GetHashCode() => _obj?.GetHashCode() ?? 0;
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
+ public override bool Equals(object obj) =>
+ obj is ValueTask &&
+ Equals((ValueTask)obj);
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask"/> value.</summary>
+ public bool Equals(ValueTask other) => _obj == other._obj && _token == other._token;
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are equal.</summary>
+ public static bool operator ==(ValueTask left, ValueTask right) =>
+ left.Equals(right);
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are not equal.</summary>
+ public static bool operator !=(ValueTask left, ValueTask right) =>
+ !left.Equals(right);
+
+ /// <summary>
+ /// Gets a <see cref="Task"/> object to represent this ValueTask.
+ /// </summary>
+ /// <remarks>
+ /// It will either return the wrapped task object if one exists, or it'll
+ /// manufacture a new task object to represent the result.
+ /// </remarks>
+ public Task AsTask() =>
+ _obj == null ? ValueTask.CompletedTask :
+ ObjectIsTask ? UnsafeGetTask() :
+ GetTaskForValueTaskSource();
+
+ /// <summary>Gets a <see cref="ValueTask"/> that may be used at any point in the future.</summary>
+ public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
+
+ /// <summary>Creates a <see cref="Task"/> to represent the <see cref="IValueTaskSource"/>.</summary>
+ private Task GetTaskForValueTaskSource()
+ {
+ IValueTaskSource t = UnsafeGetValueTaskSource();
+ ValueTaskSourceStatus status = t.GetStatus(_token);
+ if (status != ValueTaskSourceStatus.Pending)
+ {
+ try
+ {
+ // Propagate any exceptions that may have occurred, then return
+ // an already successfully completed task.
+ t.GetResult(_token);
+ return ValueTask.CompletedTask;
+
+ // If status is Faulted or Canceled, GetResult should throw. But
+ // we can't guarantee every implementation will do the "right thing".
+ // If it doesn't throw, we just treat that as success and ignore
+ // the status.
+ }
+ catch (Exception exc)
+ {
+ if (status == ValueTaskSourceStatus.Canceled)
+ {
+#if netstandard
+ var tcs = new TaskCompletionSource<bool>();
+ tcs.TrySetCanceled();
+ return tcs.Task;
+#else
+ if (exc is OperationCanceledException oce)
+ {
+ var task = new Task<VoidTaskResult>();
+ task.TrySetCanceled(oce.CancellationToken, oce);
+ return task;
+ }
+ else
+ {
+ return Task.FromCanceled(new CancellationToken(true));
+ }
+#endif
+ }
+ else
+ {
+#if netstandard
+ var tcs = new TaskCompletionSource<bool>();
+ tcs.TrySetException(exc);
+ return tcs.Task;
+#else
+ return Task.FromException(exc);
+#endif
+ }
+ }
+ }
+
+ var m = new ValueTaskSourceTask(t, _token);
+ return
+#if netstandard
+ m.Task;
+#else
+ m;
+#endif
+ }
+
+ /// <summary>Type used to create a <see cref="Task"/> to represent a <see cref="IValueTaskSource"/>.</summary>
+ private sealed class ValueTaskSourceTask :
+#if netstandard
+ TaskCompletionSource<bool>
+#else
+ Task<VoidTaskResult>
+#endif
+ {
+ private static readonly Action<object> s_completionAction = state =>
+ {
+ if (!(state is ValueTaskSourceTask vtst) ||
+ !(vtst._source is IValueTaskSource source))
+ {
+ // This could only happen if the IValueTaskSource passed the wrong state
+ // or if this callback were invoked multiple times such that the state
+ // was previously nulled out.
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
+ return;
+ }
+
+ vtst._source = null;
+ ValueTaskSourceStatus status = source.GetStatus(vtst._token);
+ try
+ {
+ source.GetResult(vtst._token);
+ vtst.TrySetResult(default);
+ }
+ catch (Exception exc)
+ {
+ if (status == ValueTaskSourceStatus.Canceled)
+ {
+#if netstandard
+ vtst.TrySetCanceled();
+#else
+ if (exc is OperationCanceledException oce)
+ {
+ vtst.TrySetCanceled(oce.CancellationToken, oce);
+ }
+ else
+ {
+ vtst.TrySetCanceled(new CancellationToken(true));
+ }
+#endif
+ }
+ else
+ {
+ vtst.TrySetException(exc);
+ }
+ }
+ };
+
+ /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
+ private IValueTaskSource _source;
+ /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
+ private readonly short _token;
+
+ public ValueTaskSourceTask(IValueTaskSource source, short token)
+ {
+ _token = token;
+ _source = source;
+ source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
+ }
+ }
+
+ /// <summary>Gets whether the <see cref="ValueTask"/> represents a completed operation.</summary>
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _obj == null || (ObjectIsTask ? UnsafeGetTask().IsCompleted : UnsafeGetValueTaskSource().GetStatus(_token) != ValueTaskSourceStatus.Pending);
+ }
+
+ /// <summary>Gets whether the <see cref="ValueTask"/> represents a successfully completed operation.</summary>
+ public bool IsCompletedSuccessfully
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get =>
+ _obj == null ||
+ (ObjectIsTask ?
+#if netstandard
+ UnsafeTask.Status == TaskStatus.RanToCompletion :
+#else
+ UnsafeGetTask().IsCompletedSuccessfully :
+#endif
+ UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Succeeded);
+ }
+
+ /// <summary>Gets whether the <see cref="ValueTask"/> represents a failed operation.</summary>
+ public bool IsFaulted
+ {
+ get =>
+ _obj != null &&
+ (ObjectIsTask ? UnsafeGetTask().IsFaulted : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Faulted);
+ }
+
+ /// <summary>Gets whether the <see cref="ValueTask"/> represents a canceled operation.</summary>
+ /// <remarks>
+ /// If the <see cref="ValueTask"/> is backed by a result or by a <see cref="IValueTaskSource"/>,
+ /// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
+ /// value of the task's <see cref="Task.IsCanceled"/> property.
+ /// </remarks>
+ public bool IsCanceled
+ {
+ get =>
+ _obj != null &&
+ (ObjectIsTask ? UnsafeGetTask().IsCanceled : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Canceled);
+ }
+
+ /// <summary>Throws the exception that caused the <see cref="ValueTask"/> to fail. If it completed successfully, nothing is thrown.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [StackTraceHidden]
+ internal void ThrowIfCompletedUnsuccessfully()
+ {
+ if (_obj != null)
+ {
+ if (ObjectIsTask)
+ {
+#if netstandard
+ UnsafeTask.GetAwaiter().GetResult();
+#else
+ TaskAwaiter.ValidateEnd(UnsafeGetTask());
+#endif
+ }
+ else
+ {
+ UnsafeGetValueTaskSource().GetResult(_token);
+ }
+ }
+ }
+
+ /// <summary>Gets an awaiter for this <see cref="ValueTask"/>.</summary>
+ public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this);
+
+ /// <summary>Configures an awaiter for this <see cref="ValueTask"/>.</summary>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
+ /// </param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
+ {
+ // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed.
+ bool avoidCapture = !continueOnCapturedContext;
+ return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, _flags | Unsafe.As<bool, ValueTaskFlags>(ref avoidCapture)));
+ }
+ }
+
+ /// <summary>Provides a value type that can represent a synchronously available value or a task object.</summary>
+ /// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <remarks>
- /// <para>
- /// Methods may return an instance of this value type when it's likely that the result of their
- /// operations will be available synchronously and when the method is expected to be invoked so
- /// frequently that the cost of allocating a new <see cref="Task{TResult}"/> for each call will
- /// be prohibitive.
- /// </para>
- /// <para>
- /// There are tradeoffs to using a <see cref="ValueTask{TResult}"/> instead of a <see cref="Task{TResult}"/>.
- /// For example, while a <see cref="ValueTask{TResult}"/> can help avoid an allocation in the case where the
- /// successful result is available synchronously, it also contains two fields whereas a <see cref="Task{TResult}"/>
- /// as a reference type is a single field. This means that a method call ends up returning two fields worth of
- /// data instead of one, which is more data to copy. It also means that if a method that returns one of these
- /// is awaited within an async method, the state machine for that async method will be larger due to needing
- /// to store the struct that's two fields instead of a single reference.
- /// </para>
- /// <para>
- /// Further, for uses other than consuming the result of an asynchronous operation via await,
- /// <see cref="ValueTask{TResult}"/> can lead to a more convoluted programming model, which can in turn actually
- /// lead to more allocations. For example, consider a method that could return either a <see cref="Task{TResult}"/>
- /// with a cached task as a common result or a <see cref="ValueTask{TResult}"/>. If the consumer of the result
- /// wants to use it as a <see cref="Task{TResult}"/>, such as to use with in methods like Task.WhenAll and Task.WhenAny,
- /// the <see cref="ValueTask{TResult}"/> would first need to be converted into a <see cref="Task{TResult}"/> using
- /// <see cref="ValueTask{TResult}.AsTask"/>, which leads to an allocation that would have been avoided if a cached
- /// <see cref="Task{TResult}"/> had been used in the first place.
- /// </para>
- /// <para>
- /// As such, the default choice for any asynchronous method should be to return a <see cref="Task"/> or
- /// <see cref="Task{TResult}"/>. Only if performance analysis proves it worthwhile should a <see cref="ValueTask{TResult}"/>
- /// be used instead of <see cref="Task{TResult}"/>. There is no non-generic version of <see cref="ValueTask{TResult}"/>
- /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where
- /// a <see cref="Task"/>-returning method completes synchronously and successfully.
- /// </para>
+ /// <see cref="ValueTask{TResult}"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
+ /// should be extracted using <see cref="AsTask"/> or <see cref="Preserve"/>. Such operations might include caching an instance to
+ /// be awaited later, registering multiple continuations with a single operation, awaiting the same task multiple times, and using
+ /// combinators over multiple operations.
/// </remarks>
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
{
- /// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary>
- internal readonly Task<TResult> _task;
+ /// <summary>null if <see cref="_result"/> has the result, otherwise a <see cref="Task{TResult}"/> or a <see cref="IValueTaskSource{TResult}"/>.</summary>
+ internal readonly object _obj;
/// <summary>The result to be used if the operation completed successfully synchronously.</summary>
internal readonly TResult _result;
+ /// <summary>Flags providing additional details about the ValueTask's contents and behavior.</summary>
+ internal readonly ValueTaskFlags _flags;
+ /// <summary>Opaque value passed through to the <see cref="IValueTaskSource{TResult}"/>.</summary>
+ internal readonly short _token;
+
+ // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation
+ // with a result of default(TResult).
- /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with the result of the successful operation.</summary>
+ /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <typeparamref name="TResult"/> result value.</summary>
/// <param name="result">The result.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(TResult result)
{
- _task = null;
_result = result;
+
+ _obj = null;
+ _flags = 0;
+ _token = 0;
}
- /// <summary>
- /// Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.
- /// </summary>
+ /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.</summary>
/// <param name="task">The task.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(Task<TResult> task)
{
if (task == null)
@@ -76,103 +405,371 @@ namespace System.Threading.Tasks
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
}
- _task = task;
- _result = default(TResult);
+ _obj = task;
+
+ _result = default;
+ _flags = ValueTaskFlags.ObjectIsTask;
+ _token = 0;
+ }
+
+ /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="IValueTaskSource{TResult}"/> object that represents the operation.</summary>
+ /// <param name="source">The source.</param>
+ /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ValueTask(IValueTaskSource<TResult> source, short token)
+ {
+ if (source == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
+ }
+
+ _obj = source;
+ _token = token;
+
+ _result = default;
+ _flags = 0;
+ }
+
+ /// <summary>Non-verified initialization of the struct to the specified values.</summary>
+ /// <param name="obj">The object.</param>
+ /// <param name="result">The result.</param>
+ /// <param name="token">The token.</param>
+ /// <param name="flags">The flags.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ValueTask(object obj, TResult result, short token, ValueTaskFlags flags)
+ {
+ _obj = obj;
+ _result = result;
+ _token = token;
+ _flags = flags;
+ }
+
+ /// <summary>Gets whether the contination should be scheduled to the current context.</summary>
+ internal bool ContinueOnCapturedContext
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (_flags & ValueTaskFlags.AvoidCapturedContext) == 0;
+ }
+
+ /// <summary>Gets whether the object in the <see cref="_obj"/> field is a <see cref="Task{TResult}"/>.</summary>
+ internal bool ObjectIsTask
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (_flags & ValueTaskFlags.ObjectIsTask) != 0;
+ }
+
+ /// <summary>Returns the <see cref="Task{TResult}"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Task<TResult> UnsafeGetTask()
+ {
+ Debug.Assert(ObjectIsTask);
+ Debug.Assert(_obj is Task<TResult>);
+ return Unsafe.As<Task<TResult>>(_obj);
+ }
+
+ /// <summary>Returns the <see cref="IValueTaskSource{TResult}"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal IValueTaskSource<TResult> UnsafeGetValueTaskSource()
+ {
+ Debug.Assert(!ObjectIsTask);
+ Debug.Assert(_obj is IValueTaskSource<TResult>);
+ return Unsafe.As<IValueTaskSource<TResult>>(_obj);
}
/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode() =>
- _task != null ? _task.GetHashCode() :
+ _obj != null ? _obj.GetHashCode() :
_result != null ? _result.GetHashCode() :
0;
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
public override bool Equals(object obj) =>
- obj is ValueTask<TResult> &&
+ obj is ValueTask<TResult> &&
Equals((ValueTask<TResult>)obj);
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
public bool Equals(ValueTask<TResult> other) =>
- _task != null || other._task != null ?
- _task == other._task :
+ _obj != null || other._obj != null ?
+ _obj == other._obj && _token == other._token :
EqualityComparer<TResult>.Default.Equals(_result, other._result);
/// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
- public static bool operator==(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ public static bool operator ==(ValueTask<TResult> left, ValueTask<TResult> right) =>
left.Equals(right);
/// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
- public static bool operator!=(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ public static bool operator !=(ValueTask<TResult> left, ValueTask<TResult> right) =>
!left.Equals(right);
/// <summary>
- /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask. It will
- /// either return the wrapped task object if one exists, or it'll manufacture a new
- /// task object to represent the result.
+ /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask.
/// </summary>
+ /// <remarks>
+ /// It will either return the wrapped task object if one exists, or it'll
+ /// manufacture a new task object to represent the result.
+ /// </remarks>
public Task<TResult> AsTask() =>
- // Return the task if we were constructed from one, otherwise manufacture one. We don't
- // cache the generated task into _task as it would end up changing both equality comparison
- // and the hash code we generate in GetHashCode.
- _task ?? AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
+ _obj == null ?
+#if netstandard
+ Task.FromResult(_result) :
+#else
+ AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result) :
+#endif
+ ObjectIsTask ? UnsafeGetTask() :
+ GetTaskForValueTaskSource();
+
+ /// <summary>Gets a <see cref="ValueTask{TResult}"/> that may be used at any point in the future.</summary>
+ public ValueTask<TResult> Preserve() => _obj == null ? this : new ValueTask<TResult>(AsTask());
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> to represent the <see cref="IValueTaskSource{TResult}"/>.</summary>
+ private Task<TResult> GetTaskForValueTaskSource()
+ {
+ IValueTaskSource<TResult> t = UnsafeGetValueTaskSource();
+ ValueTaskSourceStatus status = t.GetStatus(_token);
+ if (status != ValueTaskSourceStatus.Pending)
+ {
+ try
+ {
+ // Get the result of the operation and return a task for it.
+ // If any exception occurred, propagate it
+ return
+#if netstandard
+ Task.FromResult(t.GetResult(_token));
+#else
+ AsyncTaskMethodBuilder<TResult>.GetTaskForResult(t.GetResult(_token));
+#endif
- internal Task<TResult> AsTaskExpectNonNull() =>
- // Return the task if we were constructed from one, otherwise manufacture one.
- // Unlike AsTask(), this method is called only when we expect _task to be non-null,
- // and thus we don't want GetTaskForResult inlined.
- _task ?? GetTaskForResultNoInlining();
+ // If status is Faulted or Canceled, GetResult should throw. But
+ // we can't guarantee every implementation will do the "right thing".
+ // If it doesn't throw, we just treat that as success and ignore
+ // the status.
+ }
+ catch (Exception exc)
+ {
+ if (status == ValueTaskSourceStatus.Canceled)
+ {
+#if netstandard
+ var tcs = new TaskCompletionSource<TResult>();
+ tcs.TrySetCanceled();
+ return tcs.Task;
+#else
+ if (exc is OperationCanceledException oce)
+ {
+ var task = new Task<TResult>();
+ task.TrySetCanceled(oce.CancellationToken, oce);
+ return task;
+ }
+ else
+ {
+ return Task.FromCanceled<TResult>(new CancellationToken(true));
+ }
+#endif
+ }
+ else
+ {
+#if netstandard
+ var tcs = new TaskCompletionSource<TResult>();
+ tcs.TrySetException(exc);
+ return tcs.Task;
+#else
+ return Task.FromException<TResult>(exc);
+#endif
+ }
+ }
+ }
+
+ var m = new ValueTaskSourceTask(t, _token);
+ return
+#if netstandard
+ m.Task;
+#else
+ m;
+#endif
+ }
+
+ /// <summary>Type used to create a <see cref="Task{TResult}"/> to represent a <see cref="IValueTaskSource{TResult}"/>.</summary>
+ private sealed class ValueTaskSourceTask :
+#if netstandard
+ TaskCompletionSource<TResult>
+#else
+ Task<TResult>
+#endif
+ {
+ private static readonly Action<object> s_completionAction = state =>
+ {
+ if (!(state is ValueTaskSourceTask vtst) ||
+ !(vtst._source is IValueTaskSource<TResult> source))
+ {
+ // This could only happen if the IValueTaskSource<TResult> passed the wrong state
+ // or if this callback were invoked multiple times such that the state
+ // was previously nulled out.
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
+ return;
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private Task<TResult> GetTaskForResultNoInlining() => AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
+ vtst._source = null;
+ ValueTaskSourceStatus status = source.GetStatus(vtst._token);
+ try
+ {
+ vtst.TrySetResult(source.GetResult(vtst._token));
+ }
+ catch (Exception exc)
+ {
+ if (status == ValueTaskSourceStatus.Canceled)
+ {
+#if netstandard
+ vtst.TrySetCanceled();
+#else
+ if (exc is OperationCanceledException oce)
+ {
+ vtst.TrySetCanceled(oce.CancellationToken, oce);
+ }
+ else
+ {
+ vtst.TrySetCanceled(new CancellationToken(true));
+ }
+#endif
+ }
+ else
+ {
+ vtst.TrySetException(exc);
+ }
+ }
+ };
+
+ /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
+ private IValueTaskSource<TResult> _source;
+ /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
+ private readonly short _token;
+
+ public ValueTaskSourceTask(IValueTaskSource<TResult> source, short token)
+ {
+ _source = source;
+ _token = token;
+ source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
+ }
+ }
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
- public bool IsCompleted => _task == null || _task.IsCompleted;
+ public bool IsCompleted
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _obj == null || (ObjectIsTask ? UnsafeGetTask().IsCompleted : UnsafeGetValueTaskSource().GetStatus(_token) != ValueTaskSourceStatus.Pending);
+ }
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
- public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully;
+ public bool IsCompletedSuccessfully
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get =>
+ _obj == null ||
+ (ObjectIsTask ?
+#if netstandard
+ UnsafeTask.Status == TaskStatus.RanToCompletion :
+#else
+ UnsafeGetTask().IsCompletedSuccessfully :
+#endif
+ UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Succeeded);
+ }
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
- public bool IsFaulted => _task != null && _task.IsFaulted;
+ public bool IsFaulted
+ {
+ get =>
+ _obj != null &&
+ (ObjectIsTask ? UnsafeGetTask().IsFaulted : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Faulted);
+ }
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
- public bool IsCanceled => _task != null && _task.IsCanceled;
+ /// <remarks>
+ /// If the <see cref="ValueTask{TResult}"/> is backed by a result or by a <see cref="IValueTaskSource{TResult}"/>,
+ /// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
+ /// value of the task's <see cref="Task.IsCanceled"/> property.
+ /// </remarks>
+ public bool IsCanceled
+ {
+ get =>
+ _obj != null &&
+ (ObjectIsTask ? UnsafeGetTask().IsCanceled : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Canceled);
+ }
/// <summary>Gets the result.</summary>
- public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult();
+ public TResult Result
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if (_obj == null)
+ {
+ return _result;
+ }
- /// <summary>Gets an awaiter for this value.</summary>
+ if (ObjectIsTask)
+ {
+#if netstandard
+ return UnsafeTask.GetAwaiter().GetResult();
+#else
+ Task<TResult> t = UnsafeGetTask();
+ TaskAwaiter.ValidateEnd(t);
+ return t.ResultOnSuccess;
+#endif
+ }
+
+ return UnsafeGetValueTaskSource().GetResult(_token);
+ }
+ }
+
+ /// <summary>Gets an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
- /// <summary>Configures an awaiter for this value.</summary>
+ /// <summary>Configures an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the captured context; otherwise, false.
/// </param>
- public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
- new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
+ {
+ // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed.
+ bool avoidCapture = !continueOnCapturedContext;
+ return new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, _flags | Unsafe.As<bool, ValueTaskFlags>(ref avoidCapture)));
+ }
/// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
public override string ToString()
{
- if (_task != null)
- {
- return _task.IsCompletedSuccessfully && _task.Result != null ?
- _task.Result.ToString() :
- string.Empty;
- }
- else
+ if (IsCompletedSuccessfully)
{
- return _result != null ?
- _result.ToString() :
- string.Empty;
+ TResult result = Result;
+ if (result != null)
+ {
+ return result.ToString();
+ }
}
+
+ return string.Empty;
}
+ }
- // TODO https://github.com/dotnet/corefx/issues/22171:
- // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute.
+ /// <summary>Internal flags used in the implementation of <see cref="ValueTask"/> and <see cref="ValueTask{TResult}"/>.</summary>
+ [Flags]
+ internal enum ValueTaskFlags : byte
+ {
+ /// <summary>
+ /// Indicates that context (e.g. SynchronizationContext) should not be captured when adding
+ /// a continuation.
+ /// </summary>
+ /// <remarks>
+ /// The value here must be 0x1, to match the value of a true Boolean reinterpreted as a byte.
+ /// This only has meaning when awaiting a ValueTask, with ConfigureAwait creating a new
+ /// ValueTask setting or not setting this flag appropriately.
+ /// </remarks>
+ AvoidCapturedContext = 0x1,
- /// <summary>Creates a method builder for use with an async method.</summary>
- /// <returns>The created builder.</returns>
- [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption
- public static AsyncValueTaskMethodBuilder<TResult> CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder<TResult>.Create();
+ /// <summary>
+ /// Indicates that the ValueTask's object field stores a Task. This is used to avoid
+ /// a type check on whatever is stored in the object field.
+ /// </summary>
+ ObjectIsTask = 0x2
}
-}
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.AdjustmentRule.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.AdjustmentRule.cs
new file mode 100644
index 000000000..0e949a30e
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.AdjustmentRule.cs
@@ -0,0 +1,248 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ [Serializable]
+ public sealed class AdjustmentRule : IEquatable<AdjustmentRule>, ISerializable, IDeserializationCallback
+ {
+ private readonly DateTime _dateStart;
+ private readonly DateTime _dateEnd;
+ private readonly TimeSpan _daylightDelta;
+ private readonly TransitionTime _daylightTransitionStart;
+ private readonly TransitionTime _daylightTransitionEnd;
+ private readonly TimeSpan _baseUtcOffsetDelta; // delta from the default Utc offset (utcOffset = defaultUtcOffset + _baseUtcOffsetDelta)
+ private readonly bool _noDaylightTransitions;
+
+ public DateTime DateStart => _dateStart;
+
+ public DateTime DateEnd => _dateEnd;
+
+ public TimeSpan DaylightDelta => _daylightDelta;
+
+ public TransitionTime DaylightTransitionStart => _daylightTransitionStart;
+
+ public TransitionTime DaylightTransitionEnd => _daylightTransitionEnd;
+
+ internal TimeSpan BaseUtcOffsetDelta => _baseUtcOffsetDelta;
+
+ /// <summary>
+ /// Gets a value indicating that this AdjustmentRule fixes the time zone offset
+ /// from DateStart to DateEnd without any daylight transitions in between.
+ /// </summary>
+ internal bool NoDaylightTransitions => _noDaylightTransitions;
+
+ internal bool HasDaylightSaving =>
+ DaylightDelta != TimeSpan.Zero ||
+ (DaylightTransitionStart != default(TransitionTime) && DaylightTransitionStart.TimeOfDay != DateTime.MinValue) ||
+ (DaylightTransitionEnd != default(TransitionTime) && DaylightTransitionEnd.TimeOfDay != DateTime.MinValue.AddMilliseconds(1));
+
+ public bool Equals(AdjustmentRule other) =>
+ other != null &&
+ _dateStart == other._dateStart &&
+ _dateEnd == other._dateEnd &&
+ _daylightDelta == other._daylightDelta &&
+ _baseUtcOffsetDelta == other._baseUtcOffsetDelta &&
+ _daylightTransitionEnd.Equals(other._daylightTransitionEnd) &&
+ _daylightTransitionStart.Equals(other._daylightTransitionStart);
+
+ public override int GetHashCode() => _dateStart.GetHashCode();
+
+ private AdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ TimeSpan baseUtcOffsetDelta,
+ bool noDaylightTransitions)
+ {
+ ValidateAdjustmentRule(dateStart, dateEnd, daylightDelta,
+ daylightTransitionStart, daylightTransitionEnd, noDaylightTransitions);
+
+ _dateStart = dateStart;
+ _dateEnd = dateEnd;
+ _daylightDelta = daylightDelta;
+ _daylightTransitionStart = daylightTransitionStart;
+ _daylightTransitionEnd = daylightTransitionEnd;
+ _baseUtcOffsetDelta = baseUtcOffsetDelta;
+ _noDaylightTransitions = noDaylightTransitions;
+ }
+
+ public static AdjustmentRule CreateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd)
+ {
+ return new AdjustmentRule(
+ dateStart,
+ dateEnd,
+ daylightDelta,
+ daylightTransitionStart,
+ daylightTransitionEnd,
+ baseUtcOffsetDelta: TimeSpan.Zero,
+ noDaylightTransitions: false);
+ }
+
+ internal static AdjustmentRule CreateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ TimeSpan baseUtcOffsetDelta,
+ bool noDaylightTransitions)
+ {
+ return new AdjustmentRule(
+ dateStart,
+ dateEnd,
+ daylightDelta,
+ daylightTransitionStart,
+ daylightTransitionEnd,
+ baseUtcOffsetDelta,
+ noDaylightTransitions);
+ }
+
+ //
+ // When Windows sets the daylight transition start Jan 1st at 12:00 AM, it means the year starts with the daylight saving on.
+ // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
+ //
+ internal bool IsStartDateMarkerForBeginningOfYear() =>
+ !NoDaylightTransitions &&
+ DaylightTransitionStart.Month == 1 && DaylightTransitionStart.Day == 1 && DaylightTransitionStart.TimeOfDay.Hour == 0 &&
+ DaylightTransitionStart.TimeOfDay.Minute == 0 && DaylightTransitionStart.TimeOfDay.Second == 0 &&
+ _dateStart.Year == _dateEnd.Year;
+
+ //
+ // When Windows sets the daylight transition end Jan 1st at 12:00 AM, it means the year ends with the daylight saving on.
+ // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
+ //
+ internal bool IsEndDateMarkerForEndOfYear() =>
+ !NoDaylightTransitions &&
+ DaylightTransitionEnd.Month == 1 && DaylightTransitionEnd.Day == 1 && DaylightTransitionEnd.TimeOfDay.Hour == 0 &&
+ DaylightTransitionEnd.TimeOfDay.Minute == 0 && DaylightTransitionEnd.TimeOfDay.Second == 0 &&
+ _dateStart.Year == _dateEnd.Year;
+
+ /// <summary>
+ /// Helper function that performs all of the validation checks for the factory methods and deserialization callback.
+ /// </summary>
+ private static void ValidateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ bool noDaylightTransitions)
+ {
+ if (dateStart.Kind != DateTimeKind.Unspecified && dateStart.Kind != DateTimeKind.Utc)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateStart));
+ }
+
+ if (dateEnd.Kind != DateTimeKind.Unspecified && dateEnd.Kind != DateTimeKind.Utc)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateEnd));
+ }
+
+ if (daylightTransitionStart.Equals(daylightTransitionEnd) && !noDaylightTransitions)
+ {
+ throw new ArgumentException(SR.Argument_TransitionTimesAreIdentical, nameof(daylightTransitionEnd));
+ }
+
+ if (dateStart > dateEnd)
+ {
+ throw new ArgumentException(SR.Argument_OutOfOrderDateTimes, nameof(dateStart));
+ }
+
+ // This cannot use UtcOffsetOutOfRange to account for the scenario where Samoa moved across the International Date Line,
+ // which caused their current BaseUtcOffset to be +13. But on the other side of the line it was UTC-11 (+1 for daylight).
+ // So when trying to describe DaylightDeltas for those times, the DaylightDelta needs
+ // to be -23 (what it takes to go from UTC+13 to UTC-10)
+ if (daylightDelta.TotalHours < -23.0 || daylightDelta.TotalHours > 14.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(daylightDelta), daylightDelta, SR.ArgumentOutOfRange_UtcOffset);
+ }
+
+ if (daylightDelta.Ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(daylightDelta));
+ }
+
+ if (dateStart != DateTime.MinValue && dateStart.Kind == DateTimeKind.Unspecified && dateStart.TimeOfDay != TimeSpan.Zero)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateStart));
+ }
+
+ if (dateEnd != DateTime.MaxValue && dateEnd.Kind == DateTimeKind.Unspecified && dateEnd.TimeOfDay != TimeSpan.Zero)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateEnd));
+ }
+ }
+
+ void IDeserializationCallback.OnDeserialization(object sender)
+ {
+ // OnDeserialization is called after each instance of this class is deserialized.
+ // This callback method performs AdjustmentRule validation after being deserialized.
+
+ try
+ {
+ ValidateAdjustmentRule(_dateStart, _dateEnd, _daylightDelta,
+ _daylightTransitionStart, _daylightTransitionEnd, _noDaylightTransitions);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ info.AddValue("DateStart", _dateStart); // Do not rename (binary serialization)
+ info.AddValue("DateEnd", _dateEnd); // Do not rename (binary serialization)
+ info.AddValue("DaylightDelta", _daylightDelta); // Do not rename (binary serialization)
+ info.AddValue("DaylightTransitionStart", _daylightTransitionStart); // Do not rename (binary serialization)
+ info.AddValue("DaylightTransitionEnd", _daylightTransitionEnd); // Do not rename (binary serialization)
+ info.AddValue("BaseUtcOffsetDelta", _baseUtcOffsetDelta); // Do not rename (binary serialization)
+ info.AddValue("NoDaylightTransitions", _noDaylightTransitions); // Do not rename (binary serialization)
+ }
+
+ private AdjustmentRule(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ _dateStart = (DateTime)info.GetValue("DateStart", typeof(DateTime)); // Do not rename (binary serialization)
+ _dateEnd = (DateTime)info.GetValue("DateEnd", typeof(DateTime)); // Do not rename (binary serialization)
+ _daylightDelta = (TimeSpan)info.GetValue("DaylightDelta", typeof(TimeSpan)); // Do not rename (binary serialization)
+ _daylightTransitionStart = (TransitionTime)info.GetValue("DaylightTransitionStart", typeof(TransitionTime)); // Do not rename (binary serialization)
+ _daylightTransitionEnd = (TransitionTime)info.GetValue("DaylightTransitionEnd", typeof(TransitionTime)); // Do not rename (binary serialization)
+
+ object o = info.GetValueNoThrow("BaseUtcOffsetDelta", typeof(TimeSpan)); // Do not rename (binary serialization)
+ if (o != null)
+ {
+ _baseUtcOffsetDelta = (TimeSpan)o;
+ }
+
+ o = info.GetValueNoThrow("NoDaylightTransitions", typeof(bool)); // Do not rename (binary serialization)
+ if (o != null)
+ {
+ _noDaylightTransitions = (bool)o;
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs
new file mode 100644
index 000000000..e3e9ddbf5
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs
@@ -0,0 +1,625 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ /// <summary>
+ /// Used to serialize and deserialize TimeZoneInfo objects based on the custom string serialization format.
+ /// </summary>
+ private struct StringSerializer
+ {
+ private enum State
+ {
+ Escaped = 0,
+ NotEscaped = 1,
+ StartOfToken = 2,
+ EndOfLine = 3
+ }
+
+ private readonly string _serializedText;
+ private int _currentTokenStartIndex;
+ private State _state;
+
+ // the majority of the strings contained in the OS time zones fit in 64 chars
+ private const int InitialCapacityForString = 64;
+ private const char Esc = '\\';
+ private const char Sep = ';';
+ private const char Lhs = '[';
+ private const char Rhs = ']';
+ private const string DateTimeFormat = "MM:dd:yyyy";
+ private const string TimeOfDayFormat = "HH:mm:ss.FFF";
+
+ /// <summary>
+ /// Creates the custom serialized string representation of a TimeZoneInfo instance.
+ /// </summary>
+ public static string GetSerializedString(TimeZoneInfo zone)
+ {
+ StringBuilder serializedText = StringBuilderCache.Acquire();
+
+ //
+ // <_id>;<_baseUtcOffset>;<_displayName>;<_standardDisplayName>;<_daylightDispayName>
+ //
+ SerializeSubstitute(zone.Id, serializedText);
+ serializedText.Append(Sep);
+ serializedText.Append(zone.BaseUtcOffset.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ SerializeSubstitute(zone.DisplayName, serializedText);
+ serializedText.Append(Sep);
+ SerializeSubstitute(zone.StandardName, serializedText);
+ serializedText.Append(Sep);
+ SerializeSubstitute(zone.DaylightName, serializedText);
+ serializedText.Append(Sep);
+
+ AdjustmentRule[] rules = zone.GetAdjustmentRules();
+ foreach (AdjustmentRule rule in rules)
+ {
+ serializedText.Append(Lhs);
+ serializedText.Append(rule.DateStart.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
+ serializedText.Append(Sep);
+ serializedText.Append(rule.DateEnd.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
+ serializedText.Append(Sep);
+ serializedText.Append(rule.DaylightDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ // serialize the TransitionTime's
+ SerializeTransitionTime(rule.DaylightTransitionStart, serializedText);
+ serializedText.Append(Sep);
+ SerializeTransitionTime(rule.DaylightTransitionEnd, serializedText);
+ serializedText.Append(Sep);
+ if (rule.BaseUtcOffsetDelta != TimeSpan.Zero)
+ {
+ // Serialize it only when BaseUtcOffsetDelta has a value to reduce the impact of adding rule.BaseUtcOffsetDelta
+ serializedText.Append(rule.BaseUtcOffsetDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ }
+ if (rule.NoDaylightTransitions)
+ {
+ // Serialize it only when NoDaylightTransitions is true to reduce the impact of adding rule.NoDaylightTransitions
+ serializedText.Append('1');
+ serializedText.Append(Sep);
+ }
+ serializedText.Append(Rhs);
+ }
+ serializedText.Append(Sep);
+
+ return StringBuilderCache.GetStringAndRelease(serializedText);
+ }
+
+ /// <summary>
+ /// Instantiates a TimeZoneInfo from a custom serialized string.
+ /// </summary>
+ public static TimeZoneInfo GetDeserializedTimeZoneInfo(string source)
+ {
+ StringSerializer s = new StringSerializer(source);
+
+ string id = s.GetNextStringValue();
+ TimeSpan baseUtcOffset = s.GetNextTimeSpanValue();
+ string displayName = s.GetNextStringValue();
+ string standardName = s.GetNextStringValue();
+ string daylightName = s.GetNextStringValue();
+ AdjustmentRule[] rules = s.GetNextAdjustmentRuleArrayValue();
+
+ try
+ {
+ return new TimeZoneInfo(id, baseUtcOffset, displayName, standardName, daylightName, rules, disableDaylightSavingTime: false);
+ }
+ catch (ArgumentException ex)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, ex);
+ }
+ catch (InvalidTimeZoneException ex)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, ex);
+ }
+ }
+
+ private StringSerializer(string str)
+ {
+ _serializedText = str;
+ _currentTokenStartIndex = 0;
+ _state = State.StartOfToken;
+ }
+
+ /// <summary>
+ /// Appends the String to the StringBuilder with all of the reserved chars escaped.
+ ///
+ /// ";" -> "\;"
+ /// "[" -> "\["
+ /// "]" -> "\]"
+ /// "\" -> "\\"
+ /// </summary>
+ private static void SerializeSubstitute(string text, StringBuilder serializedText)
+ {
+ foreach (char c in text)
+ {
+ if (c == Esc || c == Lhs || c == Rhs || c == Sep)
+ {
+ serializedText.Append('\\');
+ }
+ serializedText.Append(c);
+ }
+ }
+
+ /// <summary>
+ /// Helper method to serialize a TimeZoneInfo.TransitionTime object.
+ /// </summary>
+ private static void SerializeTransitionTime(TransitionTime time, StringBuilder serializedText)
+ {
+ serializedText.Append(Lhs);
+ serializedText.Append(time.IsFixedDateRule ? '1' : '0');
+ serializedText.Append(Sep);
+ serializedText.Append(time.TimeOfDay.ToString(TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo));
+ serializedText.Append(Sep);
+ serializedText.Append(time.Month.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ if (time.IsFixedDateRule)
+ {
+ serializedText.Append(time.Day.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ }
+ else
+ {
+ serializedText.Append(time.Week.ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ serializedText.Append(((int)time.DayOfWeek).ToString(CultureInfo.InvariantCulture));
+ serializedText.Append(Sep);
+ }
+ serializedText.Append(Rhs);
+ }
+
+ /// <summary>
+ /// Helper function to determine if the passed in string token is allowed to be preceded by an escape sequence token.
+ /// </summary>
+ private static void VerifyIsEscapableCharacter(char c)
+ {
+ if (c != Esc && c != Sep && c != Lhs && c != Rhs)
+ {
+ throw new SerializationException(SR.Format(SR.Serialization_InvalidEscapeSequence, c));
+ }
+ }
+
+ /// <summary>
+ /// Helper function that reads past "v.Next" data fields. Receives a "depth" parameter indicating the
+ /// current relative nested bracket depth that _currentTokenStartIndex is at. The function ends
+ /// successfully when "depth" returns to zero (0).
+ /// </summary>
+ private void SkipVersionNextDataFields(int depth /* starting depth in the nested brackets ('[', ']')*/)
+ {
+ if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ State tokenState = State.NotEscaped;
+
+ // walk the serialized text, building up the token as we go...
+ for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++)
+ {
+ if (tokenState == State.Escaped)
+ {
+ VerifyIsEscapableCharacter(_serializedText[i]);
+ tokenState = State.NotEscaped;
+ }
+ else if (tokenState == State.NotEscaped)
+ {
+ switch (_serializedText[i])
+ {
+ case Esc:
+ tokenState = State.Escaped;
+ break;
+
+ case Lhs:
+ depth++;
+ break;
+ case Rhs:
+ depth--;
+ if (depth == 0)
+ {
+ _currentTokenStartIndex = i + 1;
+ if (_currentTokenStartIndex >= _serializedText.Length)
+ {
+ _state = State.EndOfLine;
+ }
+ else
+ {
+ _state = State.StartOfToken;
+ }
+ return;
+ }
+ break;
+
+ case '\0':
+ // invalid character
+ throw new SerializationException(SR.Serialization_InvalidData);
+
+ default:
+ break;
+ }
+ }
+ }
+
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ /// <summary>
+ /// Helper function that reads a string token from the serialized text. The function
+ /// updates <see cref="_currentTokenStartIndex"/> to point to the next token on exit.
+ /// Also <see cref="_state"/> is set to either <see cref="State.StartOfToken"/> or
+ /// <see cref="State.EndOfLine"/> on exit.
+ /// </summary>
+ private string GetNextStringValue()
+ {
+ // first verify the internal state of the object
+ if (_state == State.EndOfLine)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ State tokenState = State.NotEscaped;
+ StringBuilder token = StringBuilderCache.Acquire(InitialCapacityForString);
+
+ // walk the serialized text, building up the token as we go...
+ for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++)
+ {
+ if (tokenState == State.Escaped)
+ {
+ VerifyIsEscapableCharacter(_serializedText[i]);
+ token.Append(_serializedText[i]);
+ tokenState = State.NotEscaped;
+ }
+ else if (tokenState == State.NotEscaped)
+ {
+ switch (_serializedText[i])
+ {
+ case Esc:
+ tokenState = State.Escaped;
+ break;
+
+ case Lhs:
+ // '[' is an unexpected character
+ throw new SerializationException(SR.Serialization_InvalidData);
+
+ case Rhs:
+ // ']' is an unexpected character
+ throw new SerializationException(SR.Serialization_InvalidData);
+
+ case Sep:
+ _currentTokenStartIndex = i + 1;
+ if (_currentTokenStartIndex >= _serializedText.Length)
+ {
+ _state = State.EndOfLine;
+ }
+ else
+ {
+ _state = State.StartOfToken;
+ }
+ return StringBuilderCache.GetStringAndRelease(token);
+
+ case '\0':
+ // invalid character
+ throw new SerializationException(SR.Serialization_InvalidData);
+
+ default:
+ token.Append(_serializedText[i]);
+ break;
+ }
+ }
+ }
+ //
+ // we are at the end of the line
+ //
+ if (tokenState == State.Escaped)
+ {
+ // we are at the end of the serialized text but we are in an escaped state
+ throw new SerializationException(SR.Format(SR.Serialization_InvalidEscapeSequence, string.Empty));
+ }
+
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ /// <summary>
+ /// Helper function to read a DateTime token.
+ /// </summary>
+ private DateTime GetNextDateTimeValue(string format)
+ {
+ string token = GetNextStringValue();
+ DateTime time;
+ if (!DateTime.TryParseExact(token, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out time))
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ return time;
+ }
+
+ /// <summary>
+ /// Helper function to read a TimeSpan token.
+ /// </summary>
+ private TimeSpan GetNextTimeSpanValue()
+ {
+ int token = GetNextInt32Value();
+ try
+ {
+ return new TimeSpan(hours: 0, minutes: token, seconds: 0);
+ }
+ catch (ArgumentOutOfRangeException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+ /// <summary>
+ /// Helper function to read an Int32 token.
+ /// </summary>
+ private int GetNextInt32Value()
+ {
+ string token = GetNextStringValue();
+ int value;
+ if (!int.TryParse(token, NumberStyles.AllowLeadingSign /* "[sign]digits" */, CultureInfo.InvariantCulture, out value))
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ return value;
+ }
+
+ /// <summary>
+ /// Helper function to read an AdjustmentRule[] token.
+ /// </summary>
+ private AdjustmentRule[] GetNextAdjustmentRuleArrayValue()
+ {
+ List<AdjustmentRule> rules = new List<AdjustmentRule>(1);
+ int count = 0;
+
+ // individual AdjustmentRule array elements do not require semicolons
+ AdjustmentRule rule = GetNextAdjustmentRuleValue();
+ while (rule != null)
+ {
+ rules.Add(rule);
+ count++;
+
+ rule = GetNextAdjustmentRuleValue();
+ }
+
+ // the AdjustmentRule array must end with a separator
+ if (_state == State.EndOfLine)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ return count != 0 ? rules.ToArray() : null;
+ }
+
+ /// <summary>
+ /// Helper function to read an AdjustmentRule token.
+ /// </summary>
+ private AdjustmentRule GetNextAdjustmentRuleValue()
+ {
+ // first verify the internal state of the object
+ if (_state == State.EndOfLine)
+ {
+ return null;
+ }
+
+ if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ // check to see if the very first token we see is the separator
+ if (_serializedText[_currentTokenStartIndex] == Sep)
+ {
+ return null;
+ }
+
+ // verify the current token is a left-hand-side marker ("[")
+ if (_serializedText[_currentTokenStartIndex] != Lhs)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ _currentTokenStartIndex++;
+
+ DateTime dateStart = GetNextDateTimeValue(DateTimeFormat);
+ DateTime dateEnd = GetNextDateTimeValue(DateTimeFormat);
+ TimeSpan daylightDelta = GetNextTimeSpanValue();
+ TransitionTime daylightStart = GetNextTransitionTimeValue();
+ TransitionTime daylightEnd = GetNextTransitionTimeValue();
+ TimeSpan baseUtcOffsetDelta = TimeSpan.Zero;
+ int noDaylightTransitions = 0;
+
+ // verify that the string is now at the right-hand-side marker ("]") ...
+
+ if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ // Check if we have baseUtcOffsetDelta in the serialized string and then deserialize it
+ if ((_serializedText[_currentTokenStartIndex] >= '0' && _serializedText[_currentTokenStartIndex] <= '9') ||
+ _serializedText[_currentTokenStartIndex] == '-' || _serializedText[_currentTokenStartIndex] == '+')
+ {
+ baseUtcOffsetDelta = GetNextTimeSpanValue();
+ }
+
+ // Check if we have NoDaylightTransitions in the serialized string and then deserialize it
+ if ((_serializedText[_currentTokenStartIndex] >= '0' && _serializedText[_currentTokenStartIndex] <= '1'))
+ {
+ noDaylightTransitions = GetNextInt32Value();
+ }
+
+ if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ if (_serializedText[_currentTokenStartIndex] != Rhs)
+ {
+ // skip ahead of any "v.Next" data at the end of the AdjustmentRule
+ //
+ // FUTURE: if the serialization format is extended in the future then this
+ // code section will need to be changed to read the new fields rather
+ // than just skipping the data at the end of the [AdjustmentRule].
+ SkipVersionNextDataFields(1);
+ }
+ else
+ {
+ _currentTokenStartIndex++;
+ }
+
+ // create the AdjustmentRule from the deserialized fields ...
+
+ AdjustmentRule rule;
+ try
+ {
+ rule = AdjustmentRule.CreateAdjustmentRule(dateStart, dateEnd, daylightDelta, daylightStart, daylightEnd, baseUtcOffsetDelta, noDaylightTransitions > 0);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+
+ // finally set the state to either EndOfLine or StartOfToken for the next caller
+ if (_currentTokenStartIndex >= _serializedText.Length)
+ {
+ _state = State.EndOfLine;
+ }
+ else
+ {
+ _state = State.StartOfToken;
+ }
+ return rule;
+ }
+
+ /// <summary>
+ /// Helper function to read a TransitionTime token.
+ /// </summary>
+ private TransitionTime GetNextTransitionTimeValue()
+ {
+ // first verify the internal state of the object
+
+ if (_state == State.EndOfLine ||
+ (_currentTokenStartIndex < _serializedText.Length && _serializedText[_currentTokenStartIndex] == Rhs))
+ {
+ //
+ // we are at the end of the line or we are starting at a "]" character
+ //
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ // verify the current token is a left-hand-side marker ("[")
+
+ if (_serializedText[_currentTokenStartIndex] != Lhs)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+ _currentTokenStartIndex++;
+
+ int isFixedDate = GetNextInt32Value();
+
+ if (isFixedDate != 0 && isFixedDate != 1)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ TransitionTime transition;
+
+ DateTime timeOfDay = GetNextDateTimeValue(TimeOfDayFormat);
+ timeOfDay = new DateTime(1, 1, 1, timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
+
+ int month = GetNextInt32Value();
+
+ if (isFixedDate == 1)
+ {
+ int day = GetNextInt32Value();
+
+ try
+ {
+ transition = TransitionTime.CreateFixedDateRule(timeOfDay, month, day);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+ else
+ {
+ int week = GetNextInt32Value();
+ int dayOfWeek = GetNextInt32Value();
+
+ try
+ {
+ transition = TransitionTime.CreateFloatingDateRule(timeOfDay, month, week, (DayOfWeek)dayOfWeek);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+ // verify that the string is now at the right-hand-side marker ("]") ...
+
+ if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ if (_serializedText[_currentTokenStartIndex] != Rhs)
+ {
+ // skip ahead of any "v.Next" data at the end of the AdjustmentRule
+ //
+ // FUTURE: if the serialization format is extended in the future then this
+ // code section will need to be changed to read the new fields rather
+ // than just skipping the data at the end of the [TransitionTime].
+ SkipVersionNextDataFields(1);
+ }
+ else
+ {
+ _currentTokenStartIndex++;
+ }
+
+ // check to see if the string is now at the separator (";") ...
+ bool sepFound = false;
+ if (_currentTokenStartIndex < _serializedText.Length &&
+ _serializedText[_currentTokenStartIndex] == Sep)
+ {
+ // handle the case where we ended on a ";"
+ _currentTokenStartIndex++;
+ sepFound = true;
+ }
+
+ if (!sepFound)
+ {
+ // we MUST end on a separator
+ throw new SerializationException(SR.Serialization_InvalidData);
+ }
+
+ // finally set the state to either EndOfLine or StartOfToken for the next caller
+ if (_currentTokenStartIndex >= _serializedText.Length)
+ {
+ _state = State.EndOfLine;
+ }
+ else
+ {
+ _state = State.StartOfToken;
+ }
+ return transition;
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.TransitionTime.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.TransitionTime.cs
new file mode 100644
index 000000000..b93794262
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.TransitionTime.cs
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ [Serializable]
+ public readonly struct TransitionTime : IEquatable<TransitionTime>, ISerializable, IDeserializationCallback
+ {
+ private readonly DateTime _timeOfDay;
+ private readonly byte _month;
+ private readonly byte _week;
+ private readonly byte _day;
+ private readonly DayOfWeek _dayOfWeek;
+ private readonly bool _isFixedDateRule;
+
+ public DateTime TimeOfDay => _timeOfDay;
+
+ public int Month => _month;
+
+ public int Week => _week;
+
+ public int Day => _day;
+
+ public DayOfWeek DayOfWeek => _dayOfWeek;
+
+ public bool IsFixedDateRule => _isFixedDateRule;
+
+ public override bool Equals(object obj) =>
+ obj is TransitionTime && Equals((TransitionTime)obj);
+
+ public static bool operator ==(TransitionTime t1, TransitionTime t2) => t1.Equals(t2);
+
+ public static bool operator !=(TransitionTime t1, TransitionTime t2) => !t1.Equals(t2);
+
+ public bool Equals(TransitionTime other) =>
+ _isFixedDateRule == other._isFixedDateRule &&
+ _timeOfDay == other._timeOfDay &&
+ _month == other._month &&
+ (other._isFixedDateRule ?
+ _day == other._day :
+ _week == other._week && _dayOfWeek == other._dayOfWeek);
+
+ public override int GetHashCode() => (int)_month ^ (int)_week << 8;
+
+ private TransitionTime(DateTime timeOfDay, int month, int week, int day, DayOfWeek dayOfWeek, bool isFixedDateRule)
+ {
+ ValidateTransitionTime(timeOfDay, month, week, day, dayOfWeek);
+
+ _timeOfDay = timeOfDay;
+ _month = (byte)month;
+ _week = (byte)week;
+ _day = (byte)day;
+ _dayOfWeek = dayOfWeek;
+ _isFixedDateRule = isFixedDateRule;
+ }
+
+ public static TransitionTime CreateFixedDateRule(DateTime timeOfDay, int month, int day) =>
+ new TransitionTime(timeOfDay, month, 1, day, DayOfWeek.Sunday, isFixedDateRule: true);
+
+ public static TransitionTime CreateFloatingDateRule(DateTime timeOfDay, int month, int week, DayOfWeek dayOfWeek) =>
+ new TransitionTime(timeOfDay, month, week, 1, dayOfWeek, isFixedDateRule: false);
+
+ /// <summary>
+ /// Helper function that validates a TransitionTime instance.
+ /// </summary>
+ private static void ValidateTransitionTime(DateTime timeOfDay, int month, int week, int day, DayOfWeek dayOfWeek)
+ {
+ if (timeOfDay.Kind != DateTimeKind.Unspecified)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecified, nameof(timeOfDay));
+ }
+
+ // Month range 1-12
+ if (month < 1 || month > 12)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_MonthParam);
+ }
+
+ // Day range 1-31
+ if (day < 1 || day > 31)
+ {
+ throw new ArgumentOutOfRangeException(nameof(day), SR.ArgumentOutOfRange_DayParam);
+ }
+
+ // Week range 1-5
+ if (week < 1 || week > 5)
+ {
+ throw new ArgumentOutOfRangeException(nameof(week), SR.ArgumentOutOfRange_Week);
+ }
+
+ // DayOfWeek range 0-6
+ if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6)
+ {
+ throw new ArgumentOutOfRangeException(nameof(dayOfWeek), SR.ArgumentOutOfRange_DayOfWeek);
+ }
+
+ timeOfDay.GetDatePart(out int timeOfDayYear, out int timeOfDayMonth, out int timeOfDayDay);
+ if (timeOfDayYear != 1 || timeOfDayMonth != 1 || timeOfDayDay != 1 || (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0))
+ {
+ throw new ArgumentException(SR.Argument_DateTimeHasTicks, nameof(timeOfDay));
+ }
+ }
+
+ void IDeserializationCallback.OnDeserialization(object sender)
+ {
+ // OnDeserialization is called after each instance of this class is deserialized.
+ // This callback method performs TransitionTime validation after being deserialized.
+
+ try
+ {
+ ValidateTransitionTime(_timeOfDay, _month, _week, _day, _dayOfWeek);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ info.AddValue("TimeOfDay", _timeOfDay); // Do not rename (binary serialization)
+ info.AddValue("Month", _month); // Do not rename (binary serialization)
+ info.AddValue("Week", _week); // Do not rename (binary serialization)
+ info.AddValue("Day", _day); // Do not rename (binary serialization)
+ info.AddValue("DayOfWeek", _dayOfWeek); // Do not rename (binary serialization)
+ info.AddValue("IsFixedDateRule", _isFixedDateRule); // Do not rename (binary serialization)
+ }
+
+ private TransitionTime(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ _timeOfDay = (DateTime)info.GetValue("TimeOfDay", typeof(DateTime)); // Do not rename (binary serialization)
+ _month = (byte)info.GetValue("Month", typeof(byte)); // Do not rename (binary serialization)
+ _week = (byte)info.GetValue("Week", typeof(byte)); // Do not rename (binary serialization)
+ _day = (byte)info.GetValue("Day", typeof(byte)); // Do not rename (binary serialization)
+ _dayOfWeek = (DayOfWeek)info.GetValue("DayOfWeek", typeof(DayOfWeek)); // Do not rename (binary serialization)
+ _isFixedDateRule = (bool)info.GetValue("IsFixedDateRule", typeof(bool)); // Do not rename (binary serialization)
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Unix.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Unix.cs
new file mode 100644
index 000000000..410eaf3ff
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Unix.cs
@@ -0,0 +1,1560 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Security;
+
+using Internal.IO;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ private const string DefaultTimeZoneDirectory = "/usr/share/zoneinfo/";
+ private const string ZoneTabFileName = "zone.tab";
+ private const string TimeZoneEnvironmentVariable = "TZ";
+ private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR";
+
+ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
+ {
+ TZifHead t;
+ DateTime[] dts;
+ byte[] typeOfLocalTime;
+ TZifType[] transitionType;
+ string zoneAbbreviations;
+ bool[] StandardTime;
+ bool[] GmtTime;
+ string futureTransitionsPosixFormat;
+
+ // parse the raw TZif bytes; this method can throw ArgumentException when the data is malformed.
+ TZif_ParseRaw(data, out t, out dts, out typeOfLocalTime, out transitionType, out zoneAbbreviations, out StandardTime, out GmtTime, out futureTransitionsPosixFormat);
+
+ _id = id;
+ _displayName = LocalId;
+ _baseUtcOffset = TimeSpan.Zero;
+
+ // find the best matching baseUtcOffset and display strings based on the current utcNow value.
+ // NOTE: read the display strings from the tzfile now in case they can't be loaded later
+ // from the globalization data.
+ DateTime utcNow = DateTime.UtcNow;
+ for (int i = 0; i < dts.Length && dts[i] <= utcNow; i++)
+ {
+ int type = typeOfLocalTime[i];
+ if (!transitionType[type].IsDst)
+ {
+ _baseUtcOffset = transitionType[type].UtcOffset;
+ _standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
+ }
+ else
+ {
+ _daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
+ }
+ }
+
+ if (dts.Length == 0)
+ {
+ // time zones like Africa/Bujumbura and Etc/GMT* have no transition times but still contain
+ // TZifType entries that may contain a baseUtcOffset and display strings
+ for (int i = 0; i < transitionType.Length; i++)
+ {
+ if (!transitionType[i].IsDst)
+ {
+ _baseUtcOffset = transitionType[i].UtcOffset;
+ _standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
+ }
+ else
+ {
+ _daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
+ }
+ }
+ }
+ _displayName = _standardDisplayName;
+
+ GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, ref _displayName);
+ GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, ref _standardDisplayName);
+ GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, ref _daylightDisplayName);
+
+ // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
+ // with DateTimeOffset, SQL Server, and the W3C XML Specification
+ if (_baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ _baseUtcOffset = new TimeSpan(_baseUtcOffset.Hours, _baseUtcOffset.Minutes, 0);
+ }
+
+ if (!dstDisabled)
+ {
+ // only create the adjustment rule if DST is enabled
+ TZif_GenerateAdjustmentRules(out _adjustmentRules, _baseUtcOffset, dts, typeOfLocalTime, transitionType, StandardTime, GmtTime, futureTransitionsPosixFormat);
+ }
+
+ ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
+ }
+
+ private void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, ref string displayName)
+ {
+ if (GlobalizationMode.Invariant)
+ {
+ displayName = _standardDisplayName;
+ return;
+ }
+
+ string timeZoneDisplayName;
+ bool result = Interop.CallStringMethod(
+ (locale, id, type, stringBuilder) => Interop.Globalization.GetTimeZoneDisplayName(
+ locale,
+ id,
+ type,
+ stringBuilder,
+ stringBuilder.Capacity),
+ CultureInfo.CurrentUICulture.Name,
+ _id,
+ nameType,
+ out timeZoneDisplayName);
+
+ // If there is an unknown error, don't set the displayName field.
+ // It will be set to the abbreviation that was read out of the tzfile.
+ if (result)
+ {
+ displayName = timeZoneDisplayName;
+ }
+ }
+
+ /// <summary>
+ /// Returns a cloned array of AdjustmentRule objects
+ /// </summary>
+ public AdjustmentRule[] GetAdjustmentRules()
+ {
+ if (_adjustmentRules == null)
+ {
+ return Array.Empty<AdjustmentRule>();
+ }
+
+ // The rules we use in Unix care mostly about the start and end dates but don't fill the transition start and end info.
+ // as the rules now is public, we should fill it properly so the caller doesn't have to know how we use it internally
+ // and can use it as it is used in Windows
+
+ AdjustmentRule[] rules = new AdjustmentRule[_adjustmentRules.Length];
+
+ for (int i = 0; i < _adjustmentRules.Length; i++)
+ {
+ var rule = _adjustmentRules[i];
+ var start = rule.DateStart.Kind == DateTimeKind.Utc ?
+ // At the daylight start we didn't start the daylight saving yet then we convert to Local time
+ // by adding the _baseUtcOffset to the UTC time
+ new DateTime(rule.DateStart.Ticks + _baseUtcOffset.Ticks, DateTimeKind.Unspecified) :
+ rule.DateStart;
+ var end = rule.DateEnd.Kind == DateTimeKind.Utc ?
+ // At the daylight saving end, the UTC time is mapped to local time which is already shifted by the daylight delta
+ // we calculate the local time by adding _baseUtcOffset + DaylightDelta to the UTC time
+ new DateTime(rule.DateEnd.Ticks + _baseUtcOffset.Ticks + rule.DaylightDelta.Ticks, DateTimeKind.Unspecified) :
+ rule.DateEnd;
+
+ var startTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, start.Hour, start.Minute, start.Second), start.Month, start.Day);
+ var endTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, end.Hour, end.Minute, end.Second), end.Month, end.Day);
+
+ rules[i] = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(start.Date, end.Date, rule.DaylightDelta, startTransition, endTransition);
+ }
+
+ return rules;
+ }
+
+ private static void PopulateAllSystemTimeZones(CachedData cachedData)
+ {
+ Debug.Assert(Monitor.IsEntered(cachedData));
+
+ string timeZoneDirectory = GetTimeZoneDirectory();
+ foreach (string timeZoneId in GetTimeZoneIds(timeZoneDirectory))
+ {
+ TimeZoneInfo value;
+ Exception ex;
+ TryGetTimeZone(timeZoneId, false, out value, out ex, cachedData, alwaysFallbackToLocalMachine: true); // populate the cache
+ }
+ }
+
+ /// <summary>
+ /// Helper function for retrieving the local system time zone.
+ /// May throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException.
+ /// Assumes cachedData lock is taken.
+ /// </summary>
+ /// <returns>A new TimeZoneInfo instance.</returns>
+ private static TimeZoneInfo GetLocalTimeZone(CachedData cachedData)
+ {
+ Debug.Assert(Monitor.IsEntered(cachedData));
+
+ // Without Registry support, create the TimeZoneInfo from a TZ file
+ return GetLocalTimeZoneFromTzFile();
+ }
+
+ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out TimeZoneInfo value, out Exception e)
+ {
+ value = null;
+ e = null;
+
+ string timeZoneDirectory = GetTimeZoneDirectory();
+ string timeZoneFilePath = Path.Combine(timeZoneDirectory, id);
+ byte[] rawData;
+ try
+ {
+ rawData = File.ReadAllBytes(timeZoneFilePath);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ e = ex;
+ return TimeZoneInfoResult.SecurityException;
+ }
+ catch (FileNotFoundException ex)
+ {
+ e = ex;
+ return TimeZoneInfoResult.TimeZoneNotFoundException;
+ }
+ catch (DirectoryNotFoundException ex)
+ {
+ e = ex;
+ return TimeZoneInfoResult.TimeZoneNotFoundException;
+ }
+ catch (IOException ex)
+ {
+ e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath), ex);
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+
+ value = GetTimeZoneFromTzData(rawData, id);
+
+ if (value == null)
+ {
+ e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath));
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+
+ return TimeZoneInfoResult.Success;
+ }
+
+ /// <summary>
+ /// Returns a collection of TimeZone Id values from the zone.tab file in the timeZoneDirectory.
+ /// </summary>
+ /// <remarks>
+ /// Lines that start with # are comments and are skipped.
+ /// </remarks>
+ private static List<string> GetTimeZoneIds(string timeZoneDirectory)
+ {
+ List<string> timeZoneIds = new List<string>();
+
+ try
+ {
+ using (StreamReader sr = new StreamReader(Path.Combine(timeZoneDirectory, ZoneTabFileName), Encoding.UTF8))
+ {
+ string zoneTabFileLine;
+ while ((zoneTabFileLine = sr.ReadLine()) != null)
+ {
+ if (!string.IsNullOrEmpty(zoneTabFileLine) && zoneTabFileLine[0] != '#')
+ {
+ // the format of the line is "country-code \t coordinates \t TimeZone Id \t comments"
+
+ int firstTabIndex = zoneTabFileLine.IndexOf('\t');
+ if (firstTabIndex != -1)
+ {
+ int secondTabIndex = zoneTabFileLine.IndexOf('\t', firstTabIndex + 1);
+ if (secondTabIndex != -1)
+ {
+ string timeZoneId;
+ int startIndex = secondTabIndex + 1;
+ int thirdTabIndex = zoneTabFileLine.IndexOf('\t', startIndex);
+ if (thirdTabIndex != -1)
+ {
+ int length = thirdTabIndex - startIndex;
+ timeZoneId = zoneTabFileLine.Substring(startIndex, length);
+ }
+ else
+ {
+ timeZoneId = zoneTabFileLine.Substring(startIndex);
+ }
+
+ if (!string.IsNullOrEmpty(timeZoneId))
+ {
+ timeZoneIds.Add(timeZoneId);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (IOException) { }
+ catch (UnauthorizedAccessException) { }
+
+ return timeZoneIds;
+ }
+
+ /// <summary>
+ /// Gets the tzfile raw data for the current 'local' time zone using the following rules.
+ /// 1. Read the TZ environment variable. If it is set, use it.
+ /// 2. Look for the data in /etc/localtime.
+ /// 3. Look for the data in GetTimeZoneDirectory()/localtime.
+ /// 4. Use UTC if all else fails.
+ /// </summary>
+ private static bool TryGetLocalTzFile(out byte[] rawData, out string id)
+ {
+ rawData = null;
+ id = null;
+ string tzVariable = GetTzEnvironmentVariable();
+
+ // If the env var is null, use the localtime file
+ if (tzVariable == null)
+ {
+ return
+ TryLoadTzFile("/etc/localtime", ref rawData, ref id) ||
+ TryLoadTzFile(Path.Combine(GetTimeZoneDirectory(), "localtime"), ref rawData, ref id);
+ }
+
+ // If it's empty, use UTC (TryGetLocalTzFile() should return false).
+ if (tzVariable.Length == 0)
+ {
+ return false;
+ }
+
+ // Otherwise, use the path from the env var. If it's not absolute, make it relative
+ // to the system timezone directory
+ string tzFilePath;
+ if (tzVariable[0] != '/')
+ {
+ id = tzVariable;
+ tzFilePath = Path.Combine(GetTimeZoneDirectory(), tzVariable);
+ }
+ else
+ {
+ tzFilePath = tzVariable;
+ }
+ return TryLoadTzFile(tzFilePath, ref rawData, ref id);
+ }
+
+ private static string GetTzEnvironmentVariable()
+ {
+ string result = Environment.GetEnvironmentVariable(TimeZoneEnvironmentVariable);
+ if (!string.IsNullOrEmpty(result))
+ {
+ if (result[0] == ':')
+ {
+ // strip off the ':' prefix
+ result = result.Substring(1);
+ }
+ }
+
+ return result;
+ }
+
+ private static bool TryLoadTzFile(string tzFilePath, ref byte[] rawData, ref string id)
+ {
+ if (File.Exists(tzFilePath))
+ {
+ try
+ {
+ rawData = File.ReadAllBytes(tzFilePath);
+ if (string.IsNullOrEmpty(id))
+ {
+ id = FindTimeZoneIdUsingReadLink(tzFilePath);
+
+ if (string.IsNullOrEmpty(id))
+ {
+ id = FindTimeZoneId(rawData);
+ }
+ }
+ return true;
+ }
+ catch (IOException) { }
+ catch (SecurityException) { }
+ catch (UnauthorizedAccessException) { }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Finds the time zone id by using 'readlink' on the path to see if tzFilePath is
+ /// a symlink to a file.
+ /// </summary>
+ private static string FindTimeZoneIdUsingReadLink(string tzFilePath)
+ {
+ string id = null;
+
+ string symlinkPath = Interop.Sys.ReadLink(tzFilePath);
+ if (symlinkPath != null)
+ {
+ // Use Path.Combine to resolve links that contain a relative path (e.g. /etc/localtime).
+ symlinkPath = Path.Combine(tzFilePath, symlinkPath);
+
+ string timeZoneDirectory = GetTimeZoneDirectory();
+ if (symlinkPath.StartsWith(timeZoneDirectory, StringComparison.Ordinal))
+ {
+ id = symlinkPath.Substring(timeZoneDirectory.Length);
+ }
+ }
+
+ return id;
+ }
+
+ /// <summary>
+ /// Enumerate files
+ /// </summary>
+ private static IEnumerable<string> EnumerateFilesRecursively(string path)
+ {
+ List<string> toExplore = null; // List used as a stack
+
+ string currentPath = path;
+ for(;;)
+ {
+ using (Microsoft.Win32.SafeHandles.SafeDirectoryHandle dirHandle = Interop.Sys.OpenDir(currentPath))
+ {
+ if (dirHandle.IsInvalid)
+ {
+ throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), currentPath, isDirectory: true);
+ }
+
+ // Read each entry from the enumerator
+ Interop.Sys.DirectoryEntry dirent;
+ while (Interop.Sys.ReadDir(dirHandle, out dirent) == 0)
+ {
+ if (dirent.InodeName == "." || dirent.InodeName == "..")
+ continue;
+
+ string fullPath = Path.Combine(currentPath, dirent.InodeName);
+
+ // Get from the dir entry whether the entry is a file or directory.
+ // We classify everything as a file unless we know it to be a directory.
+ bool isDir;
+ if (dirent.InodeType == Interop.Sys.NodeType.DT_DIR)
+ {
+ // We know it's a directory.
+ isDir = true;
+ }
+ else if (dirent.InodeType == Interop.Sys.NodeType.DT_LNK || dirent.InodeType == Interop.Sys.NodeType.DT_UNKNOWN)
+ {
+ // It's a symlink or unknown: stat to it to see if we can resolve it to a directory.
+ // If we can't (e.g. symlink to a file, broken symlink, etc.), we'll just treat it as a file.
+
+ Interop.Sys.FileStatus fileinfo;
+ if (Interop.Sys.Stat(fullPath, out fileinfo) >= 0)
+ {
+ isDir = (fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
+ }
+ else
+ {
+ isDir = false;
+ }
+ }
+ else
+ {
+ // Otherwise, treat it as a file. This includes regular files, FIFOs, etc.
+ isDir = false;
+ }
+
+ // Yield the result if the user has asked for it. In the case of directories,
+ // always explore it by pushing it onto the stack, regardless of whether
+ // we're returning directories.
+ if (isDir)
+ {
+ if (toExplore == null)
+ {
+ toExplore = new List<string>();
+ }
+ toExplore.Add(fullPath);
+ }
+ else
+ {
+ yield return fullPath;
+ }
+ }
+ }
+
+ if (toExplore == null || toExplore.Count == 0)
+ break;
+
+ currentPath = toExplore[toExplore.Count - 1];
+ toExplore.RemoveAt(toExplore.Count - 1);
+ }
+ }
+
+ /// <summary>
+ /// Find the time zone id by searching all the tzfiles for the one that matches rawData
+ /// and return its file name.
+ /// </summary>
+ private static string FindTimeZoneId(byte[] rawData)
+ {
+ // default to "Local" if we can't find the right tzfile
+ string id = LocalId;
+ string timeZoneDirectory = GetTimeZoneDirectory();
+ string localtimeFilePath = Path.Combine(timeZoneDirectory, "localtime");
+ string posixrulesFilePath = Path.Combine(timeZoneDirectory, "posixrules");
+ byte[] buffer = new byte[rawData.Length];
+
+ try
+ {
+ foreach (string filePath in EnumerateFilesRecursively(timeZoneDirectory))
+ {
+ // skip the localtime and posixrules file, since they won't give us the correct id
+ if (!string.Equals(filePath, localtimeFilePath, StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(filePath, posixrulesFilePath, StringComparison.OrdinalIgnoreCase))
+ {
+ if (CompareTimeZoneFile(filePath, buffer, rawData))
+ {
+ // if all bytes are the same, this must be the right tz file
+ id = filePath;
+
+ // strip off the root time zone directory
+ if (id.StartsWith(timeZoneDirectory, StringComparison.Ordinal))
+ {
+ id = id.Substring(timeZoneDirectory.Length);
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (IOException) { }
+ catch (SecurityException) { }
+ catch (UnauthorizedAccessException) { }
+
+ return id;
+ }
+
+ private static bool CompareTimeZoneFile(string filePath, byte[] buffer, byte[] rawData)
+ {
+ try
+ {
+ // bufferSize == 1 used to avoid unnecessary buffer in FileStream
+ using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1))
+ {
+ if (stream.Length == rawData.Length)
+ {
+ int index = 0;
+ int count = rawData.Length;
+
+ while (count > 0)
+ {
+ int n = stream.Read(buffer, index, count);
+ if (n == 0)
+ throw Error.GetEndOfFile();
+
+ int end = index + n;
+ for (; index < end; index++)
+ {
+ if (buffer[index] != rawData[index])
+ {
+ return false;
+ }
+ }
+
+ count -= n;
+ }
+
+ return true;
+ }
+ }
+ }
+ catch (IOException) { }
+ catch (SecurityException) { }
+ catch (UnauthorizedAccessException) { }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Helper function used by 'GetLocalTimeZone()' - this function wraps the call
+ /// for loading time zone data from computers without Registry support.
+ ///
+ /// The TryGetLocalTzFile() call returns a Byte[] containing the compiled tzfile.
+ /// </summary>
+ private static TimeZoneInfo GetLocalTimeZoneFromTzFile()
+ {
+ byte[] rawData;
+ string id;
+ if (TryGetLocalTzFile(out rawData, out id))
+ {
+ TimeZoneInfo result = GetTimeZoneFromTzData(rawData, id);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ // if we can't find a local time zone, return UTC
+ return Utc;
+ }
+
+ private static TimeZoneInfo GetTimeZoneFromTzData(byte[] rawData, string id)
+ {
+ if (rawData != null)
+ {
+ try
+ {
+ return new TimeZoneInfo(rawData, id, dstDisabled: false); // create a TimeZoneInfo instance from the TZif data w/ DST support
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+ try
+ {
+ return new TimeZoneInfo(rawData, id, dstDisabled: true); // create a TimeZoneInfo instance from the TZif data w/o DST support
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+ }
+
+ return null;
+ }
+
+ private static string GetTimeZoneDirectory()
+ {
+ string tzDirectory = Environment.GetEnvironmentVariable(TimeZoneDirectoryEnvironmentVariable);
+
+ if (tzDirectory == null)
+ {
+ tzDirectory = DefaultTimeZoneDirectory;
+ }
+ else if (!tzDirectory.EndsWith(Path.DirectorySeparatorChar))
+ {
+ tzDirectory += Path.DirectorySeparatorChar;
+ }
+
+ return tzDirectory;
+ }
+
+ /// <summary>
+ /// Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
+ /// This function wraps the logic necessary to keep the private
+ /// SystemTimeZones cache in working order
+ ///
+ /// This function will either return a valid TimeZoneInfo instance or
+ /// it will throw 'InvalidTimeZoneException' / 'TimeZoneNotFoundException'.
+ /// </summary>
+ public static TimeZoneInfo FindSystemTimeZoneById(string id)
+ {
+ // Special case for Utc as it will not exist in the dictionary with the rest
+ // of the system time zones. There is no need to do this check for Local.Id
+ // since Local is a real time zone that exists in the dictionary cache
+ if (string.Equals(id, UtcId, StringComparison.OrdinalIgnoreCase))
+ {
+ return Utc;
+ }
+
+ if (id == null)
+ {
+ throw new ArgumentNullException(nameof(id));
+ }
+ else if (id.Length == 0 || id.Contains('\0'))
+ {
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id));
+ }
+
+ TimeZoneInfo value;
+ Exception e;
+
+ TimeZoneInfoResult result;
+
+ CachedData cachedData = s_cachedData;
+
+ lock (cachedData)
+ {
+ result = TryGetTimeZone(id, false, out value, out e, cachedData, alwaysFallbackToLocalMachine: true);
+ }
+
+ if (result == TimeZoneInfoResult.Success)
+ {
+ return value;
+ }
+ else if (result == TimeZoneInfoResult.InvalidTimeZoneException)
+ {
+ Debug.Assert(e is InvalidTimeZoneException,
+ "TryGetTimeZone must create an InvalidTimeZoneException when it returns TimeZoneInfoResult.InvalidTimeZoneException");
+ throw e;
+ }
+ else if (result == TimeZoneInfoResult.SecurityException)
+ {
+ throw new SecurityException(SR.Format(SR.Security_CannotReadFileData, id), e);
+ }
+ else
+ {
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id), e);
+ }
+ }
+
+ // DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
+ internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
+ {
+ bool isDaylightSavings;
+ // Use the standard code path for Unix since there isn't a faster way of handling current-year-only time zones
+ return GetUtcOffsetFromUtc(time, Local, out isDaylightSavings, out isAmbiguousLocalDst);
+ }
+
+ // TZFILE(5) BSD File Formats Manual TZFILE(5)
+ //
+ // NAME
+ // tzfile -- timezone information
+ //
+ // SYNOPSIS
+ // #include "/usr/src/lib/libc/stdtime/tzfile.h"
+ //
+ // DESCRIPTION
+ // The time zone information files used by tzset(3) begin with the magic
+ // characters ``TZif'' to identify them as time zone information files, fol-
+ // lowed by sixteen bytes reserved for future use, followed by four four-
+ // byte values written in a ``standard'' byte order (the high-order byte of
+ // the value is written first). These values are, in order:
+ //
+ // tzh_ttisgmtcnt The number of UTC/local indicators stored in the file.
+ // tzh_ttisstdcnt The number of standard/wall indicators stored in the
+ // file.
+ // tzh_leapcnt The number of leap seconds for which data is stored in
+ // the file.
+ // tzh_timecnt The number of ``transition times'' for which data is
+ // stored in the file.
+ // tzh_typecnt The number of ``local time types'' for which data is
+ // stored in the file (must not be zero).
+ // tzh_charcnt The number of characters of ``time zone abbreviation
+ // strings'' stored in the file.
+ //
+ // The above header is followed by tzh_timecnt four-byte values of type
+ // long, sorted in ascending order. These values are written in ``stan-
+ // dard'' byte order. Each is used as a transition time (as returned by
+ // time(3)) at which the rules for computing local time change. Next come
+ // tzh_timecnt one-byte values of type unsigned char; each one tells which
+ // of the different types of ``local time'' types described in the file is
+ // associated with the same-indexed transition time. These values serve as
+ // indices into an array of ttinfo structures that appears next in the file;
+ // these structures are defined as follows:
+ //
+ // struct ttinfo {
+ // long tt_gmtoff;
+ // int tt_isdst;
+ // unsigned int tt_abbrind;
+ // };
+ //
+ // Each structure is written as a four-byte value for tt_gmtoff of type
+ // long, in a standard byte order, followed by a one-byte value for tt_isdst
+ // and a one-byte value for tt_abbrind. In each structure, tt_gmtoff gives
+ // the number of seconds to be added to UTC, tt_isdst tells whether tm_isdst
+ // should be set by localtime(3) and tt_abbrind serves as an index into the
+ // array of time zone abbreviation characters that follow the ttinfo struc-
+ // ture(s) in the file.
+ //
+ // Then there are tzh_leapcnt pairs of four-byte values, written in standard
+ // byte order; the first value of each pair gives the time (as returned by
+ // time(3)) at which a leap second occurs; the second gives the total number
+ // of leap seconds to be applied after the given time. The pairs of values
+ // are sorted in ascending order by time.b
+ //
+ // Then there are tzh_ttisstdcnt standard/wall indicators, each stored as a
+ // one-byte value; they tell whether the transition times associated with
+ // local time types were specified as standard time or wall clock time, and
+ // are used when a time zone file is used in handling POSIX-style time zone
+ // environment variables.
+ //
+ // Finally there are tzh_ttisgmtcnt UTC/local indicators, each stored as a
+ // one-byte value; they tell whether the transition times associated with
+ // local time types were specified as UTC or local time, and are used when a
+ // time zone file is used in handling POSIX-style time zone environment
+ // variables.
+ //
+ // localtime uses the first standard-time ttinfo structure in the file (or
+ // simply the first ttinfo structure in the absence of a standard-time
+ // structure) if either tzh_timecnt is zero or the time argument is less
+ // than the first transition time recorded in the file.
+ //
+ // SEE ALSO
+ // ctime(3), time2posix(3), zic(8)
+ //
+ // BSD September 13, 1994 BSD
+ //
+ //
+ //
+ // TIME(3) BSD Library Functions Manual TIME(3)
+ //
+ // NAME
+ // time -- get time of day
+ //
+ // LIBRARY
+ // Standard C Library (libc, -lc)
+ //
+ // SYNOPSIS
+ // #include <time.h>
+ //
+ // time_t
+ // time(time_t *tloc);
+ //
+ // DESCRIPTION
+ // The time() function returns the value of time in seconds since 0 hours, 0
+ // minutes, 0 seconds, January 1, 1970, Coordinated Universal Time, without
+ // including leap seconds. If an error occurs, time() returns the value
+ // (time_t)-1.
+ //
+ // The return value is also stored in *tloc, provided that tloc is non-null.
+ //
+ // ERRORS
+ // The time() function may fail for any of the reasons described in
+ // gettimeofday(2).
+ //
+ // SEE ALSO
+ // gettimeofday(2), ctime(3)
+ //
+ // STANDARDS
+ // The time function conforms to IEEE Std 1003.1-2001 (``POSIX.1'').
+ //
+ // BUGS
+ // Neither ISO/IEC 9899:1999 (``ISO C99'') nor IEEE Std 1003.1-2001
+ // (``POSIX.1'') requires time() to set errno on failure; thus, it is impos-
+ // sible for an application to distinguish the valid time value -1 (repre-
+ // senting the last UTC second of 1969) from the error return value.
+ //
+ // Systems conforming to earlier versions of the C and POSIX standards
+ // (including older versions of FreeBSD) did not set *tloc in the error
+ // case.
+ //
+ // HISTORY
+ // A time() function appeared in Version 6 AT&T UNIX.
+ //
+ // BSD July 18, 2003 BSD
+ //
+ //
+ private static void TZif_GenerateAdjustmentRules(out AdjustmentRule[] rules, TimeSpan baseUtcOffset, DateTime[] dts, byte[] typeOfLocalTime,
+ TZifType[] transitionType, bool[] StandardTime, bool[] GmtTime, string futureTransitionsPosixFormat)
+ {
+ rules = null;
+
+ if (dts.Length > 0)
+ {
+ int index = 0;
+ List<AdjustmentRule> rulesList = new List<AdjustmentRule>();
+
+ while (index <= dts.Length)
+ {
+ TZif_GenerateAdjustmentRule(ref index, baseUtcOffset, rulesList, dts, typeOfLocalTime, transitionType, StandardTime, GmtTime, futureTransitionsPosixFormat);
+ }
+
+ rules = rulesList.ToArray();
+ if (rules != null && rules.Length == 0)
+ {
+ rules = null;
+ }
+ }
+ }
+
+ private static void TZif_GenerateAdjustmentRule(ref int index, TimeSpan timeZoneBaseUtcOffset, List<AdjustmentRule> rulesList, DateTime[] dts,
+ byte[] typeOfLocalTime, TZifType[] transitionTypes, bool[] StandardTime, bool[] GmtTime, string futureTransitionsPosixFormat)
+ {
+ // To generate AdjustmentRules, use the following approach:
+ // The first AdjustmentRule will go from DateTime.MinValue to the first transition time greater than DateTime.MinValue.
+ // Each middle AdjustmentRule wil go from dts[index-1] to dts[index].
+ // The last AdjustmentRule will go from dts[dts.Length-1] to Datetime.MaxValue.
+
+ // 0. Skip any DateTime.MinValue transition times. In newer versions of the tzfile, there
+ // is a "big bang" transition time, which is before the year 0001. Since any times before year 0001
+ // cannot be represented by DateTime, there is no reason to make AdjustmentRules for these unrepresentable time periods.
+ // 1. If there are no DateTime.MinValue times, the first AdjustmentRule goes from DateTime.MinValue
+ // to the first transition and uses the first standard transitionType (or the first transitionType if none of them are standard)
+ // 2. Create an AdjustmentRule for each transition, i.e. from dts[index - 1] to dts[index].
+ // This rule uses the transitionType[index - 1] and the whole AdjustmentRule only describes a single offset - either
+ // all daylight savings, or all stanard time.
+ // 3. After all the transitions are filled out, the last AdjustmentRule is created from either:
+ // a. a POSIX-style timezone description ("futureTransitionsPosixFormat"), if there is one or
+ // b. continue the last transition offset until DateTime.Max
+
+ while (index < dts.Length && dts[index] == DateTime.MinValue)
+ {
+ index++;
+ }
+
+ if (index == 0)
+ {
+ TZifType transitionType = TZif_GetEarlyDateTransitionType(transitionTypes);
+ DateTime endTransitionDate = dts[index];
+
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan daylightDelta = transitionType.IsDst ? transitionOffset : TimeSpan.Zero;
+ TimeSpan baseUtcDelta = transitionType.IsDst ? TimeSpan.Zero : transitionOffset;
+
+ AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(
+ DateTime.MinValue,
+ endTransitionDate.AddTicks(-1),
+ daylightDelta,
+ default(TransitionTime),
+ default(TransitionTime),
+ baseUtcDelta,
+ noDaylightTransitions: true);
+ rulesList.Add(r);
+ }
+ else if (index < dts.Length)
+ {
+ DateTime startTransitionDate = dts[index - 1];
+ TZifType startTransitionType = transitionTypes[typeOfLocalTime[index - 1]];
+
+ DateTime endTransitionDate = dts[index];
+
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(startTransitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan daylightDelta = startTransitionType.IsDst ? transitionOffset : TimeSpan.Zero;
+ TimeSpan baseUtcDelta = startTransitionType.IsDst ? TimeSpan.Zero : transitionOffset;
+
+ TransitionTime dstStart;
+ if (startTransitionType.IsDst)
+ {
+ // the TransitionTime fields are not used when AdjustmentRule.NoDaylightTransitions == true.
+ // However, there are some cases in the past where DST = true, and the daylight savings offset
+ // now equals what the current BaseUtcOffset is. In that case, the AdjustmentRule.DaylightOffset
+ // is going to be TimeSpan.Zero. But we still need to return 'true' from AdjustmentRule.HasDaylightSaving.
+ // To ensure we always return true from HasDaylightSaving, make a "special" dstStart that will make the logic
+ // in HasDaylightSaving return true.
+ dstStart = TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(2), 1, 1);
+ }
+ else
+ {
+ dstStart = default(TransitionTime);
+ }
+
+ AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(
+ startTransitionDate,
+ endTransitionDate.AddTicks(-1),
+ daylightDelta,
+ dstStart,
+ default(TransitionTime),
+ baseUtcDelta,
+ noDaylightTransitions: true);
+ rulesList.Add(r);
+ }
+ else
+ {
+ // create the AdjustmentRule that will be used for all DateTimes after the last transition
+
+ // NOTE: index == dts.Length
+ DateTime startTransitionDate = dts[index - 1];
+
+ if (!string.IsNullOrEmpty(futureTransitionsPosixFormat))
+ {
+ AdjustmentRule r = TZif_CreateAdjustmentRuleForPosixFormat(futureTransitionsPosixFormat, startTransitionDate, timeZoneBaseUtcOffset);
+ if (r != null)
+ {
+ rulesList.Add(r);
+ }
+ }
+ else
+ {
+ // just use the last transition as the rule which will be used until the end of time
+
+ TZifType transitionType = transitionTypes[typeOfLocalTime[index - 1]];
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan daylightDelta = transitionType.IsDst ? transitionOffset : TimeSpan.Zero;
+ TimeSpan baseUtcDelta = transitionType.IsDst ? TimeSpan.Zero : transitionOffset;
+
+ AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(
+ startTransitionDate,
+ DateTime.MaxValue,
+ daylightDelta,
+ default(TransitionTime),
+ default(TransitionTime),
+ baseUtcDelta,
+ noDaylightTransitions: true);
+ rulesList.Add(r);
+ }
+ }
+
+ index++;
+ }
+
+ private static TimeSpan TZif_CalculateTransitionOffsetFromBase(TimeSpan transitionOffset, TimeSpan timeZoneBaseUtcOffset)
+ {
+ TimeSpan result = transitionOffset - timeZoneBaseUtcOffset;
+
+ // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
+ // with DateTimeOffset, SQL Server, and the W3C XML Specification
+ if (result.Ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ result = new TimeSpan(result.Hours, result.Minutes, 0);
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the first standard-time transition type, or simply the first transition type
+ /// if there are no standard transition types.
+ /// </summary>>
+ /// <remarks>
+ /// from 'man tzfile':
+ /// localtime(3) uses the first standard-time ttinfo structure in the file
+ /// (or simply the first ttinfo structure in the absence of a standard-time
+ /// structure) if either tzh_timecnt is zero or the time argument is less
+ /// than the first transition time recorded in the file.
+ /// </remarks>
+ private static TZifType TZif_GetEarlyDateTransitionType(TZifType[] transitionTypes)
+ {
+ foreach (TZifType transitionType in transitionTypes)
+ {
+ if (!transitionType.IsDst)
+ {
+ return transitionType;
+ }
+ }
+
+ if (transitionTypes.Length > 0)
+ {
+ return transitionTypes[0];
+ }
+
+ throw new InvalidTimeZoneException(SR.InvalidTimeZone_NoTTInfoStructures);
+ }
+
+ /// <summary>
+ /// Creates an AdjustmentRule given the POSIX TZ environment variable string.
+ /// </summary>
+ /// <remarks>
+ /// See http://man7.org/linux/man-pages/man3/tzset.3.html for the format and semantics of this POSX string.
+ /// </remarks>
+ private static AdjustmentRule TZif_CreateAdjustmentRuleForPosixFormat(string posixFormat, DateTime startTransitionDate, TimeSpan timeZoneBaseUtcOffset)
+ {
+ string standardName;
+ string standardOffset;
+ string daylightSavingsName;
+ string daylightSavingsOffset;
+ string start;
+ string startTime;
+ string end;
+ string endTime;
+
+ if (TZif_ParsePosixFormat(posixFormat, out standardName, out standardOffset, out daylightSavingsName,
+ out daylightSavingsOffset, out start, out startTime, out end, out endTime))
+ {
+ // a valid posixFormat has at least standardName and standardOffset
+
+ TimeSpan? parsedBaseOffset = TZif_ParseOffsetString(standardOffset);
+ if (parsedBaseOffset.HasValue)
+ {
+ TimeSpan baseOffset = parsedBaseOffset.Value.Negate(); // offsets are backwards in POSIX notation
+ baseOffset = TZif_CalculateTransitionOffsetFromBase(baseOffset, timeZoneBaseUtcOffset);
+
+ // having a daylightSavingsName means there is a DST rule
+ if (!string.IsNullOrEmpty(daylightSavingsName))
+ {
+ TimeSpan? parsedDaylightSavings = TZif_ParseOffsetString(daylightSavingsOffset);
+ TimeSpan daylightSavingsTimeSpan;
+ if (!parsedDaylightSavings.HasValue)
+ {
+ // default DST to 1 hour if it isn't specified
+ daylightSavingsTimeSpan = new TimeSpan(1, 0, 0);
+ }
+ else
+ {
+ daylightSavingsTimeSpan = parsedDaylightSavings.Value.Negate(); // offsets are backwards in POSIX notation
+ daylightSavingsTimeSpan = TZif_CalculateTransitionOffsetFromBase(daylightSavingsTimeSpan, timeZoneBaseUtcOffset);
+ daylightSavingsTimeSpan = TZif_CalculateTransitionOffsetFromBase(daylightSavingsTimeSpan, baseOffset);
+ }
+
+ TransitionTime dstStart = TZif_CreateTransitionTimeFromPosixRule(start, startTime);
+ TransitionTime dstEnd = TZif_CreateTransitionTimeFromPosixRule(end, endTime);
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startTransitionDate,
+ DateTime.MaxValue,
+ daylightSavingsTimeSpan,
+ dstStart,
+ dstEnd,
+ baseOffset,
+ noDaylightTransitions: false);
+ }
+ else
+ {
+ // if there is no daylightSavingsName, the whole AdjustmentRule should be with no transitions - just the baseOffset
+ return AdjustmentRule.CreateAdjustmentRule(
+ startTransitionDate,
+ DateTime.MaxValue,
+ TimeSpan.Zero,
+ default(TransitionTime),
+ default(TransitionTime),
+ baseOffset,
+ noDaylightTransitions: true);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static TimeSpan? TZif_ParseOffsetString(string offset)
+ {
+ TimeSpan? result = null;
+
+ if (!string.IsNullOrEmpty(offset))
+ {
+ bool negative = offset[0] == '-';
+ if (negative || offset[0] == '+')
+ {
+ offset = offset.Substring(1);
+ }
+
+ // Try parsing just hours first.
+ // Note, TimeSpan.TryParseExact "%h" can't be used here because some time zones using values
+ // like "26" or "144" and TimeSpan parsing would turn that into 26 or 144 *days* instead of hours.
+ int hours;
+ if (int.TryParse(offset, out hours))
+ {
+ result = new TimeSpan(hours, 0, 0);
+ }
+ else
+ {
+ TimeSpan parsedTimeSpan;
+ if (TimeSpan.TryParseExact(offset, "g", CultureInfo.InvariantCulture, out parsedTimeSpan))
+ {
+ result = parsedTimeSpan;
+ }
+ }
+
+ if (result.HasValue && negative)
+ {
+ result = result.Value.Negate();
+ }
+ }
+
+ return result;
+ }
+
+ private static TransitionTime TZif_CreateTransitionTimeFromPosixRule(string date, string time)
+ {
+ if (string.IsNullOrEmpty(date))
+ {
+ return default(TransitionTime);
+ }
+
+ if (date[0] == 'M')
+ {
+ // Mm.w.d
+ // This specifies day d of week w of month m. The day d must be between 0(Sunday) and 6.The week w must be between 1 and 5;
+ // week 1 is the first week in which day d occurs, and week 5 specifies the last d day in the month. The month m should be between 1 and 12.
+
+ int month;
+ int week;
+ DayOfWeek day;
+ if (!TZif_ParseMDateRule(date, out month, out week, out day))
+ {
+ throw new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_UnparseablePosixMDateString, date));
+ }
+
+ DateTime timeOfDay;
+ TimeSpan? timeOffset = TZif_ParseOffsetString(time);
+ if (timeOffset.HasValue)
+ {
+ // This logic isn't correct and can't be corrected until https://github.com/dotnet/corefx/issues/2618 is fixed.
+ // Some time zones use time values like, "26", "144", or "-2".
+ // This allows the week to sometimes be week 4 and sometimes week 5 in the month.
+ // For now, strip off any 'days' in the offset, and just get the time of day correct
+ timeOffset = new TimeSpan(timeOffset.Value.Hours, timeOffset.Value.Minutes, timeOffset.Value.Seconds);
+ if (timeOffset.Value < TimeSpan.Zero)
+ {
+ timeOfDay = new DateTime(1, 1, 2, 0, 0, 0);
+ }
+ else
+ {
+ timeOfDay = new DateTime(1, 1, 1, 0, 0, 0);
+ }
+
+ timeOfDay += timeOffset.Value;
+ }
+ else
+ {
+ // default to 2AM.
+ timeOfDay = new DateTime(1, 1, 1, 2, 0, 0);
+ }
+
+ return TransitionTime.CreateFloatingDateRule(timeOfDay, month, week, day);
+ }
+ else
+ {
+ // Jn
+ // This specifies the Julian day, with n between 1 and 365.February 29 is never counted, even in leap years.
+
+ // n
+ // This specifies the Julian day, with n between 0 and 365.February 29 is counted in leap years.
+
+ // These two rules cannot be expressed with the current AdjustmentRules
+ // One of them *could* be supported if we relaxed the TransitionTime validation rules, and allowed
+ // "IsFixedDateRule = true, Month = 0, Day = n" to mean the nth day of the year, picking one of the rules above
+
+ throw new InvalidTimeZoneException(SR.InvalidTimeZone_JulianDayNotSupported);
+ }
+ }
+
+ /// <summary>
+ /// Parses a string like Mm.w.d into month, week and DayOfWeek values.
+ /// </summary>
+ /// <returns>
+ /// true if the parsing succeeded; otherwise, false.
+ /// </returns>
+ private static bool TZif_ParseMDateRule(string dateRule, out int month, out int week, out DayOfWeek dayOfWeek)
+ {
+ if (dateRule[0] == 'M')
+ {
+ int firstDotIndex = dateRule.IndexOf('.');
+ if (firstDotIndex > 0)
+ {
+ int secondDotIndex = dateRule.IndexOf('.', firstDotIndex + 1);
+ if (secondDotIndex > 0)
+ {
+ if (int.TryParse(dateRule.AsSpan().Slice(1, firstDotIndex - 1), out month) &&
+ int.TryParse(dateRule.AsSpan().Slice(firstDotIndex + 1, secondDotIndex - firstDotIndex - 1), out week) &&
+ int.TryParse(dateRule.AsSpan().Slice(secondDotIndex + 1), out int day))
+ {
+ dayOfWeek = (DayOfWeek)day;
+ return true;
+ }
+ }
+ }
+ }
+
+ month = 0;
+ week = 0;
+ dayOfWeek = default(DayOfWeek);
+ return false;
+ }
+
+ private static bool TZif_ParsePosixFormat(
+ string posixFormat,
+ out string standardName,
+ out string standardOffset,
+ out string daylightSavingsName,
+ out string daylightSavingsOffset,
+ out string start,
+ out string startTime,
+ out string end,
+ out string endTime)
+ {
+ standardName = null;
+ standardOffset = null;
+ daylightSavingsName = null;
+ daylightSavingsOffset = null;
+ start = null;
+ startTime = null;
+ end = null;
+ endTime = null;
+
+ int index = 0;
+ standardName = TZif_ParsePosixName(posixFormat, ref index);
+ standardOffset = TZif_ParsePosixOffset(posixFormat, ref index);
+
+ daylightSavingsName = TZif_ParsePosixName(posixFormat, ref index);
+ if (!string.IsNullOrEmpty(daylightSavingsName))
+ {
+ daylightSavingsOffset = TZif_ParsePosixOffset(posixFormat, ref index);
+
+ if (index < posixFormat.Length && posixFormat[index] == ',')
+ {
+ index++;
+ TZif_ParsePosixDateTime(posixFormat, ref index, out start, out startTime);
+
+ if (index < posixFormat.Length && posixFormat[index] == ',')
+ {
+ index++;
+ TZif_ParsePosixDateTime(posixFormat, ref index, out end, out endTime);
+ }
+ }
+ }
+
+ return !string.IsNullOrEmpty(standardName) && !string.IsNullOrEmpty(standardOffset);
+ }
+
+ private static string TZif_ParsePosixName(string posixFormat, ref int index)
+ {
+ bool isBracketEnclosed = index < posixFormat.Length && posixFormat[index] == '<';
+ if (isBracketEnclosed)
+ {
+ // move past the opening bracket
+ index++;
+
+ string result = TZif_ParsePosixString(posixFormat, ref index, c => c == '>');
+
+ // move past the closing bracket
+ if (index < posixFormat.Length && posixFormat[index] == '>')
+ {
+ index++;
+ }
+
+ return result;
+ }
+ else
+ {
+ return TZif_ParsePosixString(
+ posixFormat,
+ ref index,
+ c => char.IsDigit(c) || c == '+' || c == '-' || c == ',');
+ }
+ }
+
+ private static string TZif_ParsePosixOffset(string posixFormat, ref int index) =>
+ TZif_ParsePosixString(posixFormat, ref index, c => !char.IsDigit(c) && c != '+' && c != '-' && c != ':');
+
+ private static void TZif_ParsePosixDateTime(string posixFormat, ref int index, out string date, out string time)
+ {
+ time = null;
+
+ date = TZif_ParsePosixDate(posixFormat, ref index);
+ if (index < posixFormat.Length && posixFormat[index] == '/')
+ {
+ index++;
+ time = TZif_ParsePosixTime(posixFormat, ref index);
+ }
+ }
+
+ private static string TZif_ParsePosixDate(string posixFormat, ref int index) =>
+ TZif_ParsePosixString(posixFormat, ref index, c => c == '/' || c == ',');
+
+ private static string TZif_ParsePosixTime(string posixFormat, ref int index) =>
+ TZif_ParsePosixString(posixFormat, ref index, c => c == ',');
+
+ private static string TZif_ParsePosixString(string posixFormat, ref int index, Func<char, bool> breakCondition)
+ {
+ int startIndex = index;
+ for (; index < posixFormat.Length; index++)
+ {
+ char current = posixFormat[index];
+ if (breakCondition(current))
+ {
+ break;
+ }
+ }
+
+ return posixFormat.Substring(startIndex, index - startIndex);
+ }
+
+ // Returns the Substring from zoneAbbreviations starting at index and ending at '\0'
+ // zoneAbbreviations is expected to be in the form: "PST\0PDT\0PWT\0\PPT"
+ private static string TZif_GetZoneAbbreviation(string zoneAbbreviations, int index)
+ {
+ int lastIndex = zoneAbbreviations.IndexOf('\0', index);
+ return lastIndex > 0 ?
+ zoneAbbreviations.Substring(index, lastIndex - index) :
+ zoneAbbreviations.Substring(index);
+ }
+
+ // Converts an array of bytes into an int - always using standard byte order (Big Endian)
+ // per TZif file standard
+ private static unsafe int TZif_ToInt32(byte[] value, int startIndex)
+ {
+ fixed (byte* pbyte = &value[startIndex])
+ {
+ return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
+ }
+ }
+
+ // Converts an array of bytes into a long - always using standard byte order (Big Endian)
+ // per TZif file standard
+ private static unsafe long TZif_ToInt64(byte[] value, int startIndex)
+ {
+ fixed (byte* pbyte = &value[startIndex])
+ {
+ int i1 = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
+ int i2 = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | (*(pbyte + 7));
+ return (uint)i2 | ((long)i1 << 32);
+ }
+ }
+
+ private static long TZif_ToUnixTime(byte[] value, int startIndex, TZVersion version) =>
+ version != TZVersion.V1 ?
+ TZif_ToInt64(value, startIndex) :
+ TZif_ToInt32(value, startIndex);
+
+ private static DateTime TZif_UnixTimeToDateTime(long unixTime) =>
+ unixTime < DateTimeOffset.UnixMinSeconds ? DateTime.MinValue :
+ unixTime > DateTimeOffset.UnixMaxSeconds ? DateTime.MaxValue :
+ DateTimeOffset.FromUnixTimeSeconds(unixTime).UtcDateTime;
+
+ private static void TZif_ParseRaw(byte[] data, out TZifHead t, out DateTime[] dts, out byte[] typeOfLocalTime, out TZifType[] transitionType,
+ out string zoneAbbreviations, out bool[] StandardTime, out bool[] GmtTime, out string futureTransitionsPosixFormat)
+ {
+ // initialize the out parameters in case the TZifHead ctor throws
+ dts = null;
+ typeOfLocalTime = null;
+ transitionType = null;
+ zoneAbbreviations = string.Empty;
+ StandardTime = null;
+ GmtTime = null;
+ futureTransitionsPosixFormat = null;
+
+ // read in the 44-byte TZ header containing the count/length fields
+ //
+ int index = 0;
+ t = new TZifHead(data, index);
+ index += TZifHead.Length;
+
+ int timeValuesLength = 4; // the first version uses 4-bytes to specify times
+ if (t.Version != TZVersion.V1)
+ {
+ // move index past the V1 information to read the V2 information
+ index += (int)((timeValuesLength * t.TimeCount) + t.TimeCount + (6 * t.TypeCount) + ((timeValuesLength + 4) * t.LeapCount) + t.IsStdCount + t.IsGmtCount + t.CharCount);
+
+ // read the V2 header
+ t = new TZifHead(data, index);
+ index += TZifHead.Length;
+ timeValuesLength = 8; // the second version uses 8-bytes
+ }
+
+ // initialize the containers for the rest of the TZ data
+ dts = new DateTime[t.TimeCount];
+ typeOfLocalTime = new byte[t.TimeCount];
+ transitionType = new TZifType[t.TypeCount];
+ zoneAbbreviations = string.Empty;
+ StandardTime = new bool[t.TypeCount];
+ GmtTime = new bool[t.TypeCount];
+
+ // read in the UTC transition points and convert them to Windows
+ //
+ for (int i = 0; i < t.TimeCount; i++)
+ {
+ long unixTime = TZif_ToUnixTime(data, index, t.Version);
+ dts[i] = TZif_UnixTimeToDateTime(unixTime);
+ index += timeValuesLength;
+ }
+
+ // read in the Type Indices; there is a 1:1 mapping of UTC transition points to Type Indices
+ // these indices directly map to the array index in the transitionType array below
+ //
+ for (int i = 0; i < t.TimeCount; i++)
+ {
+ typeOfLocalTime[i] = data[index];
+ index += 1;
+ }
+
+ // read in the Type table. Each 6-byte entry represents
+ // {UtcOffset, IsDst, AbbreviationIndex}
+ //
+ // each AbbreviationIndex is a character index into the zoneAbbreviations string below
+ //
+ for (int i = 0; i < t.TypeCount; i++)
+ {
+ transitionType[i] = new TZifType(data, index);
+ index += 6;
+ }
+
+ // read in the Abbreviation ASCII string. This string will be in the form:
+ // "PST\0PDT\0PWT\0\PPT"
+ //
+ Encoding enc = Encoding.UTF8;
+ zoneAbbreviations = enc.GetString(data, index, (int)t.CharCount);
+ index += (int)t.CharCount;
+
+ // skip ahead of the Leap-Seconds Adjustment data. In a future release, consider adding
+ // support for Leap-Seconds
+ //
+ index += (int)(t.LeapCount * (timeValuesLength + 4)); // skip the leap second transition times
+
+ // read in the Standard Time table. There should be a 1:1 mapping between Type-Index and Standard
+ // Time table entries.
+ //
+ // TRUE = transition time is standard time
+ // FALSE = transition time is wall clock time
+ // ABSENT = transition time is wall clock time
+ //
+ for (int i = 0; i < t.IsStdCount && i < t.TypeCount && index < data.Length; i++)
+ {
+ StandardTime[i] = (data[index++] != 0);
+ }
+
+ // read in the GMT Time table. There should be a 1:1 mapping between Type-Index and GMT Time table
+ // entries.
+ //
+ // TRUE = transition time is UTC
+ // FALSE = transition time is local time
+ // ABSENT = transition time is local time
+ //
+ for (int i = 0; i < t.IsGmtCount && i < t.TypeCount && index < data.Length; i++)
+ {
+ GmtTime[i] = (data[index++] != 0);
+ }
+
+ if (t.Version != TZVersion.V1)
+ {
+ // read the POSIX-style format, which should be wrapped in newlines with the last newline at the end of the file
+ if (data[index++] == '\n' && data[data.Length - 1] == '\n')
+ {
+ futureTransitionsPosixFormat = enc.GetString(data, index, data.Length - index - 1);
+ }
+ }
+ }
+
+ private struct TZifType
+ {
+ public const int Length = 6;
+
+ public readonly TimeSpan UtcOffset;
+ public readonly bool IsDst;
+ public readonly byte AbbreviationIndex;
+
+ public TZifType(byte[] data, int index)
+ {
+ if (data == null || data.Length < index + Length)
+ {
+ throw new ArgumentException(SR.Argument_TimeZoneInfoInvalidTZif, nameof(data));
+ }
+ UtcOffset = new TimeSpan(0, 0, TZif_ToInt32(data, index + 00));
+ IsDst = (data[index + 4] != 0);
+ AbbreviationIndex = data[index + 5];
+ }
+ }
+
+ private struct TZifHead
+ {
+ public const int Length = 44;
+
+ public readonly uint Magic; // TZ_MAGIC "TZif"
+ public readonly TZVersion Version; // 1 byte for a \0 or 2 or 3
+ // public byte[15] Reserved; // reserved for future use
+ public readonly uint IsGmtCount; // number of transition time flags
+ public readonly uint IsStdCount; // number of transition time flags
+ public readonly uint LeapCount; // number of leap seconds
+ public readonly uint TimeCount; // number of transition times
+ public readonly uint TypeCount; // number of local time types
+ public readonly uint CharCount; // number of abbreviated characters
+
+ public TZifHead(byte[] data, int index)
+ {
+ if (data == null || data.Length < Length)
+ {
+ throw new ArgumentException("bad data", nameof(data));
+ }
+
+ Magic = (uint)TZif_ToInt32(data, index + 00);
+
+ if (Magic != 0x545A6966)
+ {
+ // 0x545A6966 = {0x54, 0x5A, 0x69, 0x66} = "TZif"
+ throw new ArgumentException(SR.Argument_TimeZoneInfoBadTZif, nameof(data));
+ }
+
+ byte version = data[index + 04];
+ Version =
+ version == '2' ? TZVersion.V2 :
+ version == '3' ? TZVersion.V3 :
+ TZVersion.V1; // default/fallback to V1 to guard against future, unsupported version numbers
+
+ // skip the 15 byte reserved field
+
+ // don't use the BitConverter class which parses data
+ // based on the Endianess of the machine architecture.
+ // this data is expected to always be in "standard byte order",
+ // regardless of the machine it is being processed on.
+
+ IsGmtCount = (uint)TZif_ToInt32(data, index + 20);
+ IsStdCount = (uint)TZif_ToInt32(data, index + 24);
+ LeapCount = (uint)TZif_ToInt32(data, index + 28);
+ TimeCount = (uint)TZif_ToInt32(data, index + 32);
+ TypeCount = (uint)TZif_ToInt32(data, index + 36);
+ CharCount = (uint)TZif_ToInt32(data, index + 40);
+ }
+ }
+
+ private enum TZVersion : byte
+ {
+ V1 = 0,
+ V2,
+ V3,
+ // when adding more versions, ensure all the logic using TZVersion is still correct
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Win32.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Win32.cs
new file mode 100644
index 000000000..5950c9565
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.Win32.cs
@@ -0,0 +1,999 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Security;
+using System.Text;
+using System.Threading;
+
+using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
+
+using Internal.Runtime.CompilerServices;
+
+using REG_TZI_FORMAT = Interop.Kernel32.REG_TZI_FORMAT;
+using TIME_ZONE_INFORMATION = Interop.Kernel32.TIME_ZONE_INFORMATION;
+using TIME_DYNAMIC_ZONE_INFORMATION = Interop.Kernel32.TIME_DYNAMIC_ZONE_INFORMATION;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ // registry constants for the 'Time Zones' hive
+ //
+ private const string TimeZonesRegistryHive = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones";
+ private const string DisplayValue = "Display";
+ private const string DaylightValue = "Dlt";
+ private const string StandardValue = "Std";
+ private const string MuiDisplayValue = "MUI_Display";
+ private const string MuiDaylightValue = "MUI_Dlt";
+ private const string MuiStandardValue = "MUI_Std";
+ private const string TimeZoneInfoValue = "TZI";
+ private const string FirstEntryValue = "FirstEntry";
+ private const string LastEntryValue = "LastEntry";
+
+ private const int MaxKeyLength = 255;
+
+#pragma warning disable 0420
+ private sealed partial class CachedData
+ {
+ private static TimeZoneInfo GetCurrentOneYearLocal()
+ {
+ // load the data from the OS
+ TIME_ZONE_INFORMATION timeZoneInformation;
+ uint result = Interop.Kernel32.GetTimeZoneInformation(out timeZoneInformation);
+ return result == Interop.Kernel32.TIME_ZONE_ID_INVALID ?
+ CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId) :
+ GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled: false);
+ }
+
+ private volatile OffsetAndRule _oneYearLocalFromUtc;
+
+ public OffsetAndRule GetOneYearLocalFromUtc(int year)
+ {
+ OffsetAndRule oneYearLocFromUtc = _oneYearLocalFromUtc;
+ if (oneYearLocFromUtc == null || oneYearLocFromUtc.Year != year)
+ {
+ TimeZoneInfo currentYear = GetCurrentOneYearLocal();
+ AdjustmentRule rule = currentYear._adjustmentRules == null ? null : currentYear._adjustmentRules[0];
+ oneYearLocFromUtc = new OffsetAndRule(year, currentYear.BaseUtcOffset, rule);
+ _oneYearLocalFromUtc = oneYearLocFromUtc;
+ }
+ return oneYearLocFromUtc;
+ }
+ }
+#pragma warning restore 0420
+
+ private sealed class OffsetAndRule
+ {
+ public readonly int Year;
+ public readonly TimeSpan Offset;
+ public readonly AdjustmentRule Rule;
+
+ public OffsetAndRule(int year, TimeSpan offset, AdjustmentRule rule)
+ {
+ Year = year;
+ Offset = offset;
+ Rule = rule;
+ }
+ }
+
+ /// <summary>
+ /// Returns a cloned array of AdjustmentRule objects
+ /// </summary>
+ public AdjustmentRule[] GetAdjustmentRules()
+ {
+ if (_adjustmentRules == null)
+ {
+ return Array.Empty<AdjustmentRule>();
+ }
+
+ return (AdjustmentRule[])_adjustmentRules.Clone();
+ }
+
+ private static void PopulateAllSystemTimeZones(CachedData cachedData)
+ {
+ Debug.Assert(Monitor.IsEntered(cachedData));
+
+ using (RegistryKey reg = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive, writable: false))
+ {
+ if (reg != null)
+ {
+ foreach (string keyName in reg.GetSubKeyNames())
+ {
+ TimeZoneInfo value;
+ Exception ex;
+ TryGetTimeZone(keyName, false, out value, out ex, cachedData); // populate the cache
+ }
+ }
+ }
+ }
+
+ private TimeZoneInfo(in TIME_ZONE_INFORMATION zone, bool dstDisabled)
+ {
+ string standardName = zone.GetStandardName();
+ if (standardName.Length == 0)
+ {
+ _id = LocalId; // the ID must contain at least 1 character - initialize _id to "Local"
+ }
+ else
+ {
+ _id = standardName;
+ }
+ _baseUtcOffset = new TimeSpan(0, -(zone.Bias), 0);
+
+ if (!dstDisabled)
+ {
+ // only create the adjustment rule if DST is enabled
+ REG_TZI_FORMAT regZone = new REG_TZI_FORMAT(zone);
+ AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(regZone, DateTime.MinValue.Date, DateTime.MaxValue.Date, zone.Bias);
+ if (rule != null)
+ {
+ _adjustmentRules = new[] { rule };
+ }
+ }
+
+ ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
+ _displayName = standardName;
+ _standardDisplayName = standardName;
+ _daylightDisplayName = zone.GetDaylightName();
+ }
+
+ /// <summary>
+ /// Helper function to check if the current TimeZoneInformation struct does not support DST.
+ /// This check returns true when the DaylightDate == StandardDate.
+ /// This check is only meant to be used for "Local".
+ /// </summary>
+ private static bool CheckDaylightSavingTimeNotSupported(in TIME_ZONE_INFORMATION timeZone) =>
+ timeZone.DaylightDate.Equals(timeZone.StandardDate);
+
+ /// <summary>
+ /// Converts a REG_TZI_FORMAT struct to an AdjustmentRule.
+ /// </summary>
+ private static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(in REG_TZI_FORMAT timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
+ {
+ bool supportsDst = timeZoneInformation.StandardDate.Month != 0;
+
+ if (!supportsDst)
+ {
+ if (timeZoneInformation.Bias == defaultBaseUtcOffset)
+ {
+ // this rule will not contain any information to be used to adjust dates. just ignore it
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ TimeSpan.Zero, // no daylight saving transition
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0), // Bias delta is all what we need from this rule
+ noDaylightTransitions: false);
+ }
+
+ //
+ // Create an AdjustmentRule with TransitionTime objects
+ //
+ TransitionTime daylightTransitionStart;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, readStartDate: true))
+ {
+ return null;
+ }
+
+ TransitionTime daylightTransitionEnd;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, readStartDate: false))
+ {
+ return null;
+ }
+
+ if (daylightTransitionStart.Equals(daylightTransitionEnd))
+ {
+ // this happens when the time zone does support DST but the OS has DST disabled
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ new TimeSpan(0, -timeZoneInformation.DaylightBias, 0),
+ daylightTransitionStart,
+ daylightTransitionEnd,
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0),
+ noDaylightTransitions: false);
+ }
+
+ /// <summary>
+ /// Helper function that searches the registry for a time zone entry
+ /// that matches the TimeZoneInformation struct.
+ /// </summary>
+ private static string FindIdFromTimeZoneInformation(in TIME_ZONE_INFORMATION timeZone, out bool dstDisabled)
+ {
+ dstDisabled = false;
+
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive, writable: false))
+ {
+ if (key == null)
+ {
+ return null;
+ }
+
+ foreach (string keyName in key.GetSubKeyNames())
+ {
+ if (TryCompareTimeZoneInformationToRegistry(timeZone, keyName, out dstDisabled))
+ {
+ return keyName;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Helper function for retrieving the local system time zone.
+ /// May throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException.
+ /// Assumes cachedData lock is taken.
+ /// </summary>
+ /// <returns>A new TimeZoneInfo instance.</returns>
+ private static TimeZoneInfo GetLocalTimeZone(CachedData cachedData)
+ {
+ Debug.Assert(Monitor.IsEntered(cachedData));
+
+ //
+ // Try using the "kernel32!GetDynamicTimeZoneInformation" API to get the "id"
+ //
+ var dynamicTimeZoneInformation = new TIME_DYNAMIC_ZONE_INFORMATION();
+
+ // call kernel32!GetDynamicTimeZoneInformation...
+ uint result = Interop.Kernel32.GetDynamicTimeZoneInformation(out dynamicTimeZoneInformation);
+ if (result == Interop.Kernel32.TIME_ZONE_ID_INVALID)
+ {
+ // return a dummy entry
+ return CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId);
+ }
+
+ // check to see if we can use the key name returned from the API call
+ string dynamicTimeZoneKeyName = dynamicTimeZoneInformation.GetTimeZoneKeyName();
+ if (dynamicTimeZoneKeyName.Length != 0)
+ {
+ TimeZoneInfo zone;
+ Exception ex;
+
+ if (TryGetTimeZone(dynamicTimeZoneKeyName, dynamicTimeZoneInformation.DynamicDaylightTimeDisabled != 0, out zone, out ex, cachedData) == TimeZoneInfoResult.Success)
+ {
+ // successfully loaded the time zone from the registry
+ return zone;
+ }
+ }
+
+ var timeZoneInformation = new TIME_ZONE_INFORMATION(dynamicTimeZoneInformation);
+
+ // the key name was not returned or it pointed to a bogus entry - search for the entry ourselves
+ string id = FindIdFromTimeZoneInformation(timeZoneInformation, out bool dstDisabled);
+
+ if (id != null)
+ {
+ TimeZoneInfo zone;
+ Exception ex;
+ if (TryGetTimeZone(id, dstDisabled, out zone, out ex, cachedData) == TimeZoneInfoResult.Success)
+ {
+ // successfully loaded the time zone from the registry
+ return zone;
+ }
+ }
+
+ // We could not find the data in the registry. Fall back to using
+ // the data from the Win32 API
+ return GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled);
+ }
+
+ /// <summary>
+ /// Helper function used by 'GetLocalTimeZone()' - this function wraps a bunch of
+ /// try/catch logic for handling the TimeZoneInfo private constructor that takes
+ /// a TIME_ZONE_INFORMATION structure.
+ /// </summary>
+ private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(in TIME_ZONE_INFORMATION timeZoneInformation, bool dstDisabled)
+ {
+ // first try to create the TimeZoneInfo with the original 'dstDisabled' flag
+ try
+ {
+ return new TimeZoneInfo(timeZoneInformation, dstDisabled);
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+
+ // if 'dstDisabled' was false then try passing in 'true' as a last ditch effort
+ if (!dstDisabled)
+ {
+ try
+ {
+ return new TimeZoneInfo(timeZoneInformation, dstDisabled: true);
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+ }
+
+ // the data returned from Windows is completely bogus; return a dummy entry
+ return CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId);
+ }
+
+ /// <summary>
+ /// Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
+ /// This function wraps the logic necessary to keep the private
+ /// SystemTimeZones cache in working order
+ ///
+ /// This function will either return a valid TimeZoneInfo instance or
+ /// it will throw 'InvalidTimeZoneException' / 'TimeZoneNotFoundException'.
+ /// </summary>
+ public static TimeZoneInfo FindSystemTimeZoneById(string id)
+ {
+ // Special case for Utc as it will not exist in the dictionary with the rest
+ // of the system time zones. There is no need to do this check for Local.Id
+ // since Local is a real time zone that exists in the dictionary cache
+ if (string.Equals(id, UtcId, StringComparison.OrdinalIgnoreCase))
+ {
+ return Utc;
+ }
+
+ if (id == null)
+ {
+ throw new ArgumentNullException(nameof(id));
+ }
+ if (id.Length == 0 || id.Length > MaxKeyLength || id.Contains('\0'))
+ {
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id));
+ }
+
+ TimeZoneInfo value;
+ Exception e;
+
+ TimeZoneInfoResult result;
+
+ CachedData cachedData = s_cachedData;
+
+ lock (cachedData)
+ {
+ result = TryGetTimeZone(id, false, out value, out e, cachedData);
+ }
+
+ if (result == TimeZoneInfoResult.Success)
+ {
+ return value;
+ }
+ else if (result == TimeZoneInfoResult.InvalidTimeZoneException)
+ {
+ throw new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidRegistryData, id), e);
+ }
+ else if (result == TimeZoneInfoResult.SecurityException)
+ {
+ throw new SecurityException(SR.Format(SR.Security_CannotReadRegistryData, id), e);
+ }
+ else
+ {
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id), e);
+ }
+ }
+
+ // DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
+ internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
+ {
+ bool isDaylightSavings = false;
+ isAmbiguousLocalDst = false;
+ TimeSpan baseOffset;
+ int timeYear = time.Year;
+
+ OffsetAndRule match = s_cachedData.GetOneYearLocalFromUtc(timeYear);
+ baseOffset = match.Offset;
+
+ if (match.Rule != null)
+ {
+ baseOffset = baseOffset + match.Rule.BaseUtcOffsetDelta;
+ if (match.Rule.HasDaylightSaving)
+ {
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
+ baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
+ }
+ }
+ return baseOffset;
+ }
+
+ /// <summary>
+ /// Converts a REG_TZI_FORMAT struct to a TransitionTime
+ /// - When the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
+ /// - When the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
+ /// </summary>
+ private static bool TransitionTimeFromTimeZoneInformation(in REG_TZI_FORMAT timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
+ {
+ //
+ // SYSTEMTIME -
+ //
+ // If the time zone does not support daylight saving time or if the caller needs
+ // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
+ // must be zero. If this date is specified, the DaylightDate value in the
+ // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system
+ // assumes the time zone data is invalid and no changes will be applied.
+ //
+ bool supportsDst = (timeZoneInformation.StandardDate.Month != 0);
+
+ if (!supportsDst)
+ {
+ transitionTime = default(TransitionTime);
+ return false;
+ }
+
+ //
+ // SYSTEMTIME -
+ //
+ // * FixedDateRule -
+ // If the Year member is not zero, the transition date is absolute; it will only occur one time
+ //
+ // * FloatingDateRule -
+ // To select the correct day in the month, set the Year member to zero, the Hour and Minute
+ // members to the transition time, the DayOfWeek member to the appropriate weekday, and the
+ // Day member to indicate the occurence of the day of the week within the month (first through fifth).
+ //
+ // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows:
+ // Hour = 2,
+ // Month = 4,
+ // DayOfWeek = 0,
+ // Day = 1.
+ //
+ // Specify 2:00a.m. on the last Thursday in October as follows:
+ // Hour = 2,
+ // Month = 10,
+ // DayOfWeek = 4,
+ // Day = 5.
+ //
+ if (readStartDate)
+ {
+ //
+ // read the "daylightTransitionStart"
+ //
+ if (timeZoneInformation.DaylightDate.Year == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.DaylightDate.Hour,
+ timeZoneInformation.DaylightDate.Minute,
+ timeZoneInformation.DaylightDate.Second,
+ timeZoneInformation.DaylightDate.Milliseconds),
+ timeZoneInformation.DaylightDate.Month,
+ timeZoneInformation.DaylightDate.Day, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.DaylightDate.DayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.DaylightDate.Hour,
+ timeZoneInformation.DaylightDate.Minute,
+ timeZoneInformation.DaylightDate.Second,
+ timeZoneInformation.DaylightDate.Milliseconds),
+ timeZoneInformation.DaylightDate.Month,
+ timeZoneInformation.DaylightDate.Day);
+ }
+ }
+ else
+ {
+ //
+ // read the "daylightTransitionEnd"
+ //
+ if (timeZoneInformation.StandardDate.Year == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.StandardDate.Hour,
+ timeZoneInformation.StandardDate.Minute,
+ timeZoneInformation.StandardDate.Second,
+ timeZoneInformation.StandardDate.Milliseconds),
+ timeZoneInformation.StandardDate.Month,
+ timeZoneInformation.StandardDate.Day, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.StandardDate.DayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.StandardDate.Hour,
+ timeZoneInformation.StandardDate.Minute,
+ timeZoneInformation.StandardDate.Second,
+ timeZoneInformation.StandardDate.Milliseconds),
+ timeZoneInformation.StandardDate.Month,
+ timeZoneInformation.StandardDate.Day);
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Helper function that takes:
+ /// 1. A string representing a <time_zone_name> registry key name.
+ /// 2. A REG_TZI_FORMAT struct containing the default rule.
+ /// 3. An AdjustmentRule[] out-parameter.
+ /// </summary>
+ private static bool TryCreateAdjustmentRules(string id, in REG_TZI_FORMAT defaultTimeZoneInformation, out AdjustmentRule[] rules, out Exception e, int defaultBaseUtcOffset)
+ {
+ rules = null;
+ e = null;
+
+ try
+ {
+ // Optional, Dynamic Time Zone Registry Data
+ // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ //
+ // HKLM
+ // Software
+ // Microsoft
+ // Windows NT
+ // CurrentVersion
+ // Time Zones
+ // <time_zone_name>
+ // Dynamic DST
+ // * "FirstEntry" REG_DWORD "1980"
+ // First year in the table. If the current year is less than this value,
+ // this entry will be used for DST boundaries
+ // * "LastEntry" REG_DWORD "2038"
+ // Last year in the table. If the current year is greater than this value,
+ // this entry will be used for DST boundaries"
+ // * "<year1>" REG_BINARY REG_TZI_FORMAT
+ // * "<year2>" REG_BINARY REG_TZI_FORMAT
+ // * "<year3>" REG_BINARY REG_TZI_FORMAT
+ //
+ using (RegistryKey dynamicKey = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive + "\\" + id + "\\Dynamic DST", writable: false))
+ {
+ if (dynamicKey == null)
+ {
+ AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(
+ defaultTimeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ rules = new[] { rule };
+ }
+ return true;
+ }
+
+ //
+ // loop over all of the "<time_zone_name>\Dynamic DST" hive entries
+ //
+ // read FirstEntry {MinValue - (year1, 12, 31)}
+ // read MiddleEntry {(yearN, 1, 1) - (yearN, 12, 31)}
+ // read LastEntry {(yearN, 1, 1) - MaxValue }
+
+ // read the FirstEntry and LastEntry key values (ex: "1980", "2038")
+ int first = (int)dynamicKey.GetValue(FirstEntryValue, -1, RegistryValueOptions.None);
+ int last = (int)dynamicKey.GetValue(LastEntryValue, -1, RegistryValueOptions.None);
+
+ if (first == -1 || last == -1 || first > last)
+ {
+ return false;
+ }
+
+ // read the first year entry
+ REG_TZI_FORMAT dtzi;
+
+ if (!TryGetTimeZoneEntryFromRegistry(dynamicKey, first.ToString(CultureInfo.InvariantCulture), out dtzi))
+ {
+ return false;
+ }
+
+ if (first == last)
+ {
+ // there is just 1 dynamic rule for this time zone.
+ AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(dtzi, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ rules = new[] { rule };
+ }
+ return true;
+ }
+
+ List<AdjustmentRule> rulesList = new List<AdjustmentRule>(1);
+
+ // there are more than 1 dynamic rules for this time zone.
+ AdjustmentRule firstRule = CreateAdjustmentRuleFromTimeZoneInformation(
+ dtzi,
+ DateTime.MinValue.Date, // MinValue
+ new DateTime(first, 12, 31), // December 31, <FirstYear>
+ defaultBaseUtcOffset);
+
+ if (firstRule != null)
+ {
+ rulesList.Add(firstRule);
+ }
+
+ // read the middle year entries
+ for (int i = first + 1; i < last; i++)
+ {
+ if (!TryGetTimeZoneEntryFromRegistry(dynamicKey, i.ToString(CultureInfo.InvariantCulture), out dtzi))
+ {
+ return false;
+ }
+ AdjustmentRule middleRule = CreateAdjustmentRuleFromTimeZoneInformation(
+ dtzi,
+ new DateTime(i, 1, 1), // January 01, <Year>
+ new DateTime(i, 12, 31), // December 31, <Year>
+ defaultBaseUtcOffset);
+
+ if (middleRule != null)
+ {
+ rulesList.Add(middleRule);
+ }
+ }
+
+ // read the last year entry
+ if (!TryGetTimeZoneEntryFromRegistry(dynamicKey, last.ToString(CultureInfo.InvariantCulture), out dtzi))
+ {
+ return false;
+ }
+ AdjustmentRule lastRule = CreateAdjustmentRuleFromTimeZoneInformation(
+ dtzi,
+ new DateTime(last, 1, 1), // January 01, <LastYear>
+ DateTime.MaxValue.Date, // MaxValue
+ defaultBaseUtcOffset);
+
+ if (lastRule != null)
+ {
+ rulesList.Add(lastRule);
+ }
+
+ // convert the List to an AdjustmentRule array
+ if (rulesList.Count != 0)
+ {
+ rules = rulesList.ToArray();
+ }
+ } // end of: using (RegistryKey dynamicKey...
+ }
+ catch (InvalidCastException ex)
+ {
+ // one of the RegistryKey.GetValue calls could not be cast to an expected value type
+ e = ex;
+ return false;
+ }
+ catch (ArgumentOutOfRangeException ex)
+ {
+ e = ex;
+ return false;
+ }
+ catch (ArgumentException ex)
+ {
+ e = ex;
+ return false;
+ }
+ return true;
+ }
+
+ private unsafe static bool TryGetTimeZoneEntryFromRegistry(RegistryKey key, string name, out REG_TZI_FORMAT dtzi)
+ {
+ byte[] regValue = key.GetValue(name, null, RegistryValueOptions.None) as byte[];
+ if (regValue == null || regValue.Length != sizeof(REG_TZI_FORMAT))
+ {
+ dtzi = default;
+ return false;
+ }
+ fixed (byte * pBytes = &regValue[0])
+ dtzi = *(REG_TZI_FORMAT *)pBytes;
+ return true;
+ }
+
+ /// <summary>
+ /// Helper function that compares the StandardBias and StandardDate portion a
+ /// TimeZoneInformation struct to a time zone registry entry.
+ /// </summary>
+ private static bool TryCompareStandardDate(in TIME_ZONE_INFORMATION timeZone, in REG_TZI_FORMAT registryTimeZoneInfo) =>
+ timeZone.Bias == registryTimeZoneInfo.Bias &&
+ timeZone.StandardBias == registryTimeZoneInfo.StandardBias &&
+ timeZone.StandardDate.Equals(registryTimeZoneInfo.StandardDate);
+
+ /// <summary>
+ /// Helper function that compares a TimeZoneInformation struct to a time zone registry entry.
+ /// </summary>
+ private static bool TryCompareTimeZoneInformationToRegistry(in TIME_ZONE_INFORMATION timeZone, string id, out bool dstDisabled)
+ {
+ dstDisabled = false;
+
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive + "\\" + id, writable: false))
+ {
+ if (key == null)
+ {
+ return false;
+ }
+
+ REG_TZI_FORMAT registryTimeZoneInfo;
+ if (!TryGetTimeZoneEntryFromRegistry(key, TimeZoneInfoValue, out registryTimeZoneInfo))
+ {
+ return false;
+ }
+
+ //
+ // first compare the bias and standard date information between the data from the Win32 API
+ // and the data from the registry...
+ //
+ bool result = TryCompareStandardDate(timeZone, registryTimeZoneInfo);
+
+ if (!result)
+ {
+ return false;
+ }
+
+ result = dstDisabled || CheckDaylightSavingTimeNotSupported(timeZone) ||
+ //
+ // since Daylight Saving Time is not "disabled", do a straight comparision between
+ // the Win32 API data and the registry data ...
+ //
+ (timeZone.DaylightBias == registryTimeZoneInfo.DaylightBias &&
+ timeZone.DaylightDate.Equals(registryTimeZoneInfo.DaylightDate));
+
+ // Finally compare the "StandardName" string value...
+ //
+ // we do not compare "DaylightName" as this TimeZoneInformation field may contain
+ // either "StandardName" or "DaylightName" depending on the time of year and current machine settings
+ //
+ if (result)
+ {
+ string registryStandardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
+ result = string.Equals(registryStandardName, timeZone.GetStandardName(), StringComparison.Ordinal);
+ }
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Helper function for retrieving a localized string resource via MUI.
+ /// The function expects a string in the form: "@resource.dll, -123"
+ ///
+ /// "resource.dll" is a language-neutral portable executable (LNPE) file in
+ /// the %windir%\system32 directory. The OS is queried to find the best-fit
+ /// localized resource file for this LNPE (ex: %windir%\system32\en-us\resource.dll.mui).
+ /// If a localized resource file exists, we LoadString resource ID "123" and
+ /// return it to our caller.
+ /// </summary>
+ private static string TryGetLocalizedNameByMuiNativeResource(string resource)
+ {
+ if (string.IsNullOrEmpty(resource))
+ {
+ return string.Empty;
+ }
+
+ // parse "@tzres.dll, -100"
+ //
+ // filePath = "C:\Windows\System32\tzres.dll"
+ // resourceId = -100
+ //
+ string[] resources = resource.Split(',');
+ if (resources.Length != 2)
+ {
+ return string.Empty;
+ }
+
+ string filePath;
+ int resourceId;
+
+ // get the path to Windows\System32
+ string system32 = Environment.SystemDirectory;
+
+ // trim the string "@tzres.dll" => "tzres.dll"
+ string tzresDll = resources[0].TrimStart('@');
+
+ try
+ {
+ filePath = Path.Combine(system32, tzresDll);
+ }
+ catch (ArgumentException)
+ {
+ // there were probably illegal characters in the path
+ return string.Empty;
+ }
+
+ if (!int.TryParse(resources[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out resourceId))
+ {
+ return string.Empty;
+ }
+ resourceId = -resourceId;
+
+ try
+ {
+ StringBuilder fileMuiPath = StringBuilderCache.Acquire(Interop.Kernel32.MAX_PATH);
+ fileMuiPath.Length = Interop.Kernel32.MAX_PATH;
+ int fileMuiPathLength = Interop.Kernel32.MAX_PATH;
+ int languageLength = 0;
+ long enumerator = 0;
+
+ bool succeeded = Interop.Kernel32.GetFileMUIPath(
+ Interop.Kernel32.MUI_PREFERRED_UI_LANGUAGES,
+ filePath, null /* language */, ref languageLength,
+ fileMuiPath, ref fileMuiPathLength, ref enumerator);
+ if (!succeeded)
+ {
+ StringBuilderCache.Release(fileMuiPath);
+ return string.Empty;
+ }
+ return TryGetLocalizedNameByNativeResource(StringBuilderCache.GetStringAndRelease(fileMuiPath), resourceId);
+ }
+ catch (EntryPointNotFoundException)
+ {
+ return string.Empty;
+ }
+ }
+
+ /// <summary>
+ /// Helper function for retrieving a localized string resource via a native resource DLL.
+ /// The function expects a string in the form: "C:\Windows\System32\en-us\resource.dll"
+ ///
+ /// "resource.dll" is a language-specific resource DLL.
+ /// If the localized resource DLL exists, LoadString(resource) is returned.
+ /// </summary>
+ private static string TryGetLocalizedNameByNativeResource(string filePath, int resource)
+ {
+ using (SafeLibraryHandle handle =
+ Interop.Kernel32.LoadLibraryEx(filePath, IntPtr.Zero, Interop.Kernel32.LOAD_LIBRARY_AS_DATAFILE))
+ {
+ if (!handle.IsInvalid)
+ {
+ const int LoadStringMaxLength = 500;
+
+ StringBuilder localizedResource = StringBuilderCache.Acquire(LoadStringMaxLength);
+
+ int result = Interop.User32.LoadString(handle, resource,
+ localizedResource, LoadStringMaxLength);
+
+ if (result != 0)
+ {
+ return StringBuilderCache.GetStringAndRelease(localizedResource);
+ }
+ }
+ }
+ return string.Empty;
+ }
+
+ /// <summary>
+ /// Helper function for retrieving the DisplayName, StandardName, and DaylightName from the registry
+ ///
+ /// The function first checks the MUI_ key-values, and if they exist, it loads the strings from the MUI
+ /// resource dll(s). When the keys do not exist, the function falls back to reading from the standard
+ /// key-values
+ /// </summary>
+ private static void GetLocalizedNamesByRegistryKey(RegistryKey key, out string displayName, out string standardName, out string daylightName)
+ {
+ displayName = string.Empty;
+ standardName = string.Empty;
+ daylightName = string.Empty;
+
+ // read the MUI_ registry keys
+ string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty, RegistryValueOptions.None) as string;
+ string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty, RegistryValueOptions.None) as string;
+ string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty, RegistryValueOptions.None) as string;
+
+ // try to load the strings from the native resource DLL(s)
+ if (!string.IsNullOrEmpty(displayNameMuiResource))
+ {
+ displayName = TryGetLocalizedNameByMuiNativeResource(displayNameMuiResource);
+ }
+
+ if (!string.IsNullOrEmpty(standardNameMuiResource))
+ {
+ standardName = TryGetLocalizedNameByMuiNativeResource(standardNameMuiResource);
+ }
+
+ if (!string.IsNullOrEmpty(daylightNameMuiResource))
+ {
+ daylightName = TryGetLocalizedNameByMuiNativeResource(daylightNameMuiResource);
+ }
+
+ // fallback to using the standard registry keys
+ if (string.IsNullOrEmpty(displayName))
+ {
+ displayName = key.GetValue(DisplayValue, string.Empty, RegistryValueOptions.None) as string;
+ }
+ if (string.IsNullOrEmpty(standardName))
+ {
+ standardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
+ }
+ if (string.IsNullOrEmpty(daylightName))
+ {
+ daylightName = key.GetValue(DaylightValue, string.Empty, RegistryValueOptions.None) as string;
+ }
+ }
+
+ /// <summary>
+ /// Helper function that takes a string representing a <time_zone_name> registry key name
+ /// and returns a TimeZoneInfo instance.
+ /// </summary>
+ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out TimeZoneInfo value, out Exception e)
+ {
+ e = null;
+
+ // Standard Time Zone Registry Data
+ // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ // HKLM
+ // Software
+ // Microsoft
+ // Windows NT
+ // CurrentVersion
+ // Time Zones
+ // <time_zone_name>
+ // * STD, REG_SZ "Standard Time Name"
+ // (For OS installed zones, this will always be English)
+ // * MUI_STD, REG_SZ "@tzres.dll,-1234"
+ // Indirect string to localized resource for Standard Time,
+ // add "%windir%\system32\" after "@"
+ // * DLT, REG_SZ "Daylight Time Name"
+ // (For OS installed zones, this will always be English)
+ // * MUI_DLT, REG_SZ "@tzres.dll,-1234"
+ // Indirect string to localized resource for Daylight Time,
+ // add "%windir%\system32\" after "@"
+ // * Display, REG_SZ "Display Name like (GMT-8:00) Pacific Time..."
+ // * MUI_Display, REG_SZ "@tzres.dll,-1234"
+ // Indirect string to localized resource for the Display,
+ // add "%windir%\system32\" after "@"
+ // * TZI, REG_BINARY REG_TZI_FORMAT
+ //
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive + "\\" + id, writable: false))
+ {
+ if (key == null)
+ {
+ value = null;
+ return TimeZoneInfoResult.TimeZoneNotFoundException;
+ }
+
+ REG_TZI_FORMAT defaultTimeZoneInformation;
+ if (!TryGetTimeZoneEntryFromRegistry(key, TimeZoneInfoValue, out defaultTimeZoneInformation))
+ {
+ // the registry value could not be cast to a byte array
+ value = null;
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+
+ AdjustmentRule[] adjustmentRules;
+ if (!TryCreateAdjustmentRules(id, defaultTimeZoneInformation, out adjustmentRules, out e, defaultTimeZoneInformation.Bias))
+ {
+ value = null;
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+
+ GetLocalizedNamesByRegistryKey(key, out string displayName, out string standardName, out string daylightName);
+
+ try
+ {
+ value = new TimeZoneInfo(
+ id,
+ new TimeSpan(0, -(defaultTimeZoneInformation.Bias), 0),
+ displayName,
+ standardName,
+ daylightName,
+ adjustmentRules,
+ disableDaylightSavingTime: false);
+
+ return TimeZoneInfoResult.Success;
+ }
+ catch (ArgumentException ex)
+ {
+ // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
+ value = null;
+ e = ex;
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ catch (InvalidTimeZoneException ex)
+ {
+ // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
+ value = null;
+ e = ex;
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/TimeZoneInfo.cs b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.cs
new file mode 100644
index 000000000..6e27376b6
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/TimeZoneInfo.cs
@@ -0,0 +1,1993 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace System
+{
+ //
+ // DateTime uses TimeZoneInfo under the hood for IsDaylightSavingTime, IsAmbiguousTime, and GetUtcOffset.
+ // These TimeZoneInfo APIs can throw ArgumentException when an Invalid-Time is passed in. To avoid this
+ // unwanted behavior in DateTime public APIs, DateTime internally passes the
+ // TimeZoneInfoOptions.NoThrowOnInvalidTime flag to internal TimeZoneInfo APIs.
+ //
+ // In the future we can consider exposing similar options on the public TimeZoneInfo APIs if there is enough
+ // demand for this alternate behavior.
+ //
+ [Flags]
+ internal enum TimeZoneInfoOptions
+ {
+ None = 1,
+ NoThrowOnInvalidTime = 2
+ };
+
+ [Serializable]
+ [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public sealed partial class TimeZoneInfo : IEquatable<TimeZoneInfo>, ISerializable, IDeserializationCallback
+ {
+ private enum TimeZoneInfoResult
+ {
+ Success = 0,
+ TimeZoneNotFoundException = 1,
+ InvalidTimeZoneException = 2,
+ SecurityException = 3
+ };
+
+ private readonly string _id;
+ private readonly string _displayName;
+ private readonly string _standardDisplayName;
+ private readonly string _daylightDisplayName;
+ private readonly TimeSpan _baseUtcOffset;
+ private readonly bool _supportsDaylightSavingTime;
+ private readonly AdjustmentRule[] _adjustmentRules;
+
+ // constants for TimeZoneInfo.Local and TimeZoneInfo.Utc
+ private const string UtcId = "UTC";
+ private const string LocalId = "Local";
+
+ private static readonly TimeZoneInfo s_utcTimeZone = CreateCustomTimeZone(UtcId, TimeSpan.Zero, UtcId, UtcId);
+
+ private static CachedData s_cachedData = new CachedData();
+
+ //
+ // All cached data are encapsulated in a helper class to allow consistent view even when the data are refreshed using ClearCachedData()
+ //
+ // For example, TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData. Without the consistent snapshot,
+ // there is a chance that the internal ConvertTime calls will throw since 'source' won't be reference equal to the new TimeZoneInfo.Local.
+ //
+#pragma warning disable 0420
+ private sealed partial class CachedData
+ {
+ private volatile TimeZoneInfo _localTimeZone;
+
+ private TimeZoneInfo CreateLocal()
+ {
+ lock (this)
+ {
+ TimeZoneInfo timeZone = _localTimeZone;
+ if (timeZone == null)
+ {
+ timeZone = GetLocalTimeZone(this);
+
+ // this step is to break the reference equality
+ // between TimeZoneInfo.Local and a second time zone
+ // such as "Pacific Standard Time"
+ timeZone = new TimeZoneInfo(
+ timeZone._id,
+ timeZone._baseUtcOffset,
+ timeZone._displayName,
+ timeZone._standardDisplayName,
+ timeZone._daylightDisplayName,
+ timeZone._adjustmentRules,
+ disableDaylightSavingTime: false);
+
+ _localTimeZone = timeZone;
+ }
+ return timeZone;
+ }
+ }
+
+ public TimeZoneInfo Local
+ {
+ get
+ {
+ TimeZoneInfo timeZone = _localTimeZone;
+ if (timeZone == null)
+ {
+ timeZone = CreateLocal();
+ }
+ return timeZone;
+ }
+ }
+
+ /// <summary>
+ /// Helper function that returns the corresponding DateTimeKind for this TimeZoneInfo.
+ /// </summary>
+ public DateTimeKind GetCorrespondingKind(TimeZoneInfo timeZone)
+ {
+ // We check reference equality to see if 'this' is the same as
+ // TimeZoneInfo.Local or TimeZoneInfo.Utc. This check is needed to
+ // support setting the DateTime Kind property to 'Local' or
+ // 'Utc' on the ConverTime(...) return value.
+ //
+ // Using reference equality instead of value equality was a
+ // performance based design compromise. The reference equality
+ // has much greater performance, but it reduces the number of
+ // returned DateTime's that can be properly set as 'Local' or 'Utc'.
+ //
+ // For example, the user could be converting to the TimeZoneInfo returned
+ // by FindSystemTimeZoneById("Pacific Standard Time") and their local
+ // machine may be in Pacific time. If we used value equality to determine
+ // the corresponding Kind then this conversion would be tagged as 'Local';
+ // where as we are currently tagging the returned DateTime as 'Unspecified'
+ // in this example. Only when the user passes in TimeZoneInfo.Local or
+ // TimeZoneInfo.Utc to the ConvertTime(...) methods will this check succeed.
+ //
+ return
+ ReferenceEquals(timeZone, s_utcTimeZone) ? DateTimeKind.Utc :
+ ReferenceEquals(timeZone, _localTimeZone) ? DateTimeKind.Local :
+ DateTimeKind.Unspecified;
+ }
+
+ public Dictionary<string, TimeZoneInfo> _systemTimeZones;
+ public ReadOnlyCollection<TimeZoneInfo> _readOnlySystemTimeZones;
+ public bool _allSystemTimeZonesRead;
+ };
+#pragma warning restore 0420
+
+ // used by GetUtcOffsetFromUtc (DateTime.Now, DateTime.ToLocalTime) for max/min whole-day range checks
+ private static readonly DateTime s_maxDateOnly = new DateTime(9999, 12, 31);
+ private static readonly DateTime s_minDateOnly = new DateTime(1, 1, 2);
+
+ public string Id => _id;
+
+ public string DisplayName => _displayName ?? string.Empty;
+
+ public string StandardName => _standardDisplayName ?? string.Empty;
+
+ public string DaylightName => _daylightDisplayName ?? string.Empty;
+
+ public TimeSpan BaseUtcOffset => _baseUtcOffset;
+
+ public bool SupportsDaylightSavingTime => _supportsDaylightSavingTime;
+
+ /// <summary>
+ /// Returns an array of TimeSpan objects representing all of
+ /// possible UTC offset values for this ambiguous time.
+ /// </summary>
+ public TimeSpan[] GetAmbiguousTimeOffsets(DateTimeOffset dateTimeOffset)
+ {
+ if (!SupportsDaylightSavingTime)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset));
+ }
+
+ DateTime adjustedTime = ConvertTime(dateTimeOffset, this).DateTime;
+
+ bool isAmbiguous = false;
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
+ if (rule != null && rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
+ isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
+ }
+
+ if (!isAmbiguous)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset));
+ }
+
+ // the passed in dateTime is ambiguous in this TimeZoneInfo instance
+ TimeSpan[] timeSpans = new TimeSpan[2];
+
+ TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta;
+
+ // the TimeSpan array must be sorted from least to greatest
+ if (rule.DaylightDelta > TimeSpan.Zero)
+ {
+ timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
+ timeSpans[1] = actualUtcOffset + rule.DaylightDelta;
+ }
+ else
+ {
+ timeSpans[0] = actualUtcOffset + rule.DaylightDelta;
+ timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
+ }
+ return timeSpans;
+ }
+
+ /// <summary>
+ /// Returns an array of TimeSpan objects representing all of
+ /// possible UTC offset values for this ambiguous time.
+ /// </summary>
+ public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
+ {
+ if (!SupportsDaylightSavingTime)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime));
+ }
+
+ DateTime adjustedTime;
+ if (dateTime.Kind == DateTimeKind.Local)
+ {
+ CachedData cachedData = s_cachedData;
+ adjustedTime = ConvertTime(dateTime, cachedData.Local, this, TimeZoneInfoOptions.None, cachedData);
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ CachedData cachedData = s_cachedData;
+ adjustedTime = ConvertTime(dateTime, s_utcTimeZone, this, TimeZoneInfoOptions.None, cachedData);
+ }
+ else
+ {
+ adjustedTime = dateTime;
+ }
+
+ bool isAmbiguous = false;
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
+ if (rule != null && rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
+ isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
+ }
+
+ if (!isAmbiguous)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime));
+ }
+
+ // the passed in dateTime is ambiguous in this TimeZoneInfo instance
+ TimeSpan[] timeSpans = new TimeSpan[2];
+ TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta;
+
+ // the TimeSpan array must be sorted from least to greatest
+ if (rule.DaylightDelta > TimeSpan.Zero)
+ {
+ timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
+ timeSpans[1] = actualUtcOffset + rule.DaylightDelta;
+ }
+ else
+ {
+ timeSpans[0] = actualUtcOffset + rule.DaylightDelta;
+ timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
+ }
+ return timeSpans;
+ }
+
+ // note the time is already adjusted
+ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime, out int? ruleIndex)
+ {
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
+ if (rule != null && rule.NoDaylightTransitions && !rule.HasDaylightSaving)
+ {
+ // When using NoDaylightTransitions rules, each rule is only for one offset.
+ // When looking for the Daylight savings rules, and we found the non-DST rule,
+ // then we get the rule right before this rule.
+ return GetPreviousAdjustmentRule(rule, ruleIndex);
+ }
+
+ return rule;
+ }
+
+ /// <summary>
+ /// Gets the AdjustmentRule that is immediately preceding the specified rule.
+ /// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
+ /// then the specified rule is returned.
+ /// </summary>
+ private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule, int? ruleIndex)
+ {
+ Debug.Assert(rule.NoDaylightTransitions, "GetPreviousAdjustmentRule should only be used with NoDaylightTransitions rules.");
+
+ if (ruleIndex.HasValue && 0 < ruleIndex.Value && ruleIndex.Value < _adjustmentRules.Length)
+ {
+ return _adjustmentRules[ruleIndex.Value - 1];
+ }
+
+ AdjustmentRule result = rule;
+ for (int i = 1; i < _adjustmentRules.Length; i++)
+ {
+ // use ReferenceEquals here instead of AdjustmentRule.Equals because
+ // ReferenceEquals is much faster. This is safe because all the callers
+ // of GetPreviousAdjustmentRule pass in a rule that was retrieved from
+ // _adjustmentRules. A different approach will be needed if this ever changes.
+ if (ReferenceEquals(rule, _adjustmentRules[i]))
+ {
+ result = _adjustmentRules[i - 1];
+ break;
+ }
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance.
+ /// </summary>
+ public TimeSpan GetUtcOffset(DateTimeOffset dateTimeOffset) =>
+ GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this);
+
+ /// <summary>
+ /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance.
+ /// </summary>
+ public TimeSpan GetUtcOffset(DateTime dateTime) =>
+ GetUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData);
+
+ // Shortcut for TimeZoneInfo.Local.GetUtcOffset
+ internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ CachedData cachedData = s_cachedData;
+ return cachedData.Local.GetUtcOffset(dateTime, flags, cachedData);
+ }
+
+ /// <summary>
+ /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance.
+ /// </summary>
+ internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) =>
+ GetUtcOffset(dateTime, flags, s_cachedData);
+
+ private TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData)
+ {
+ if (dateTime.Kind == DateTimeKind.Local)
+ {
+ if (cachedData.GetCorrespondingKind(this) != DateTimeKind.Local)
+ {
+ //
+ // normal case of converting from Local to Utc and then getting the offset from the UTC DateTime
+ //
+ DateTime adjustedTime = ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags);
+ return GetUtcOffsetFromUtc(adjustedTime, this);
+ }
+
+ //
+ // Fall through for TimeZoneInfo.Local.GetUtcOffset(date)
+ // to handle an edge case with Invalid-Times for DateTime formatting:
+ //
+ // Consider the invalid PST time "2007-03-11T02:00:00.0000000-08:00"
+ //
+ // By directly calling GetUtcOffset instead of converting to UTC and then calling GetUtcOffsetFromUtc
+ // the correct invalid offset of "-08:00" is returned. In the normal case of converting to UTC as an
+ // interim-step, the invalid time is adjusted into a *valid* UTC time which causes a change in output:
+ //
+ // 1) invalid PST time "2007-03-11T02:00:00.0000000-08:00"
+ // 2) converted to UTC "2007-03-11T10:00:00.0000000Z"
+ // 3) offset returned "2007-03-11T03:00:00.0000000-07:00"
+ //
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc)
+ {
+ return _baseUtcOffset;
+ }
+ else
+ {
+ //
+ // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a
+ // special Loss-Less case.
+ //
+ return GetUtcOffsetFromUtc(dateTime, this);
+ }
+ }
+
+ return GetUtcOffset(dateTime, this, flags);
+ }
+
+ /// <summary>
+ /// Returns true if the time is during the ambiguous time period
+ /// for the current TimeZoneInfo instance.
+ /// </summary>
+ public bool IsAmbiguousTime(DateTimeOffset dateTimeOffset)
+ {
+ if (!_supportsDaylightSavingTime)
+ {
+ return false;
+ }
+
+ DateTimeOffset adjustedTime = ConvertTime(dateTimeOffset, this);
+ return IsAmbiguousTime(adjustedTime.DateTime);
+ }
+
+ /// <summary>
+ /// Returns true if the time is during the ambiguous time period
+ /// for the current TimeZoneInfo instance.
+ /// </summary>
+ public bool IsAmbiguousTime(DateTime dateTime) =>
+ IsAmbiguousTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+
+ /// <summary>
+ /// Returns true if the time is during the ambiguous time period
+ /// for the current TimeZoneInfo instance.
+ /// </summary>
+ internal bool IsAmbiguousTime(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ if (!_supportsDaylightSavingTime)
+ {
+ return false;
+ }
+
+ CachedData cachedData = s_cachedData;
+ DateTime adjustedTime =
+ dateTime.Kind == DateTimeKind.Local ? ConvertTime(dateTime, cachedData.Local, this, flags, cachedData) :
+ dateTime.Kind == DateTimeKind.Utc ? ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData) :
+ dateTime;
+
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
+ if (rule != null && rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
+ return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance.
+ /// </summary>
+ public bool IsDaylightSavingTime(DateTimeOffset dateTimeOffset)
+ {
+ bool isDaylightSavingTime;
+ GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this, out isDaylightSavingTime);
+ return isDaylightSavingTime;
+ }
+
+ /// <summary>
+ /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance.
+ /// </summary>
+ public bool IsDaylightSavingTime(DateTime dateTime) =>
+ IsDaylightSavingTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData);
+
+ /// <summary>
+ /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance.
+ /// </summary>
+ internal bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags) =>
+ IsDaylightSavingTime(dateTime, flags, s_cachedData);
+
+ private bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData)
+ {
+ //
+ // dateTime.Kind is UTC, then time will be converted from UTC
+ // into current instance's timezone
+ // dateTime.Kind is Local, then time will be converted from Local
+ // into current instance's timezone
+ // dateTime.Kind is UnSpecified, then time is already in
+ // current instance's timezone
+ //
+ // Our DateTime handles ambiguous times, (one is in the daylight and
+ // one is in standard.) If a new DateTime is constructed during ambiguous
+ // time, it is defaulted to "Standard" (i.e. this will return false).
+ // For Invalid times, we will return false
+
+ if (!_supportsDaylightSavingTime || _adjustmentRules == null)
+ {
+ return false;
+ }
+
+ DateTime adjustedTime;
+ //
+ // handle any Local/Utc special cases...
+ //
+ if (dateTime.Kind == DateTimeKind.Local)
+ {
+ adjustedTime = ConvertTime(dateTime, cachedData.Local, this, flags, cachedData);
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc)
+ {
+ // simple always false case: TimeZoneInfo.Utc.IsDaylightSavingTime(dateTime, flags);
+ return false;
+ }
+ else
+ {
+ //
+ // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a
+ // special Loss-Less case.
+ //
+ bool isDaylightSavings;
+ GetUtcOffsetFromUtc(dateTime, this, out isDaylightSavings);
+ return isDaylightSavings;
+ }
+ }
+ else
+ {
+ adjustedTime = dateTime;
+ }
+
+ //
+ // handle the normal cases...
+ //
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
+ if (rule != null && rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
+ return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Returns true when dateTime falls into a "hole in time".
+ /// </summary>
+ public bool IsInvalidTime(DateTime dateTime)
+ {
+ bool isInvalid = false;
+
+ if ((dateTime.Kind == DateTimeKind.Unspecified) ||
+ (dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local))
+ {
+ // only check Unspecified and (Local when this TimeZoneInfo instance is Local)
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime, out ruleIndex);
+
+ if (rule != null && rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule, ruleIndex);
+ isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
+ }
+ else
+ {
+ isInvalid = false;
+ }
+ }
+
+ return isInvalid;
+ }
+
+ /// <summary>
+ /// Clears data from static members.
+ /// </summary>
+ public static void ClearCachedData()
+ {
+ // Clear a fresh instance of cached data
+ s_cachedData = new CachedData();
+ }
+
+ /// <summary>
+ /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone.
+ /// </summary>
+ public static DateTimeOffset ConvertTimeBySystemTimeZoneId(DateTimeOffset dateTimeOffset, string destinationTimeZoneId) =>
+ ConvertTime(dateTimeOffset, FindSystemTimeZoneById(destinationTimeZoneId));
+
+ /// <summary>
+ /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone.
+ /// </summary>
+ public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, string destinationTimeZoneId) =>
+ ConvertTime(dateTime, FindSystemTimeZoneById(destinationTimeZoneId));
+
+ /// <summary>
+ /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone.
+ /// </summary>
+ public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
+ {
+ if (dateTime.Kind == DateTimeKind.Local && string.Equals(sourceTimeZoneId, Local.Id, StringComparison.OrdinalIgnoreCase))
+ {
+ // TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData.
+ // Take snapshot of cached data to guarantee this method will not be impacted by the ClearCachedData call.
+ // Without the snapshot, there is a chance that ConvertTime will throw since 'source' won't
+ // be reference equal to the new TimeZoneInfo.Local
+ //
+ CachedData cachedData = s_cachedData;
+ return ConvertTime(dateTime, cachedData.Local, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, cachedData);
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc && string.Equals(sourceTimeZoneId, Utc.Id, StringComparison.OrdinalIgnoreCase))
+ {
+ return ConvertTime(dateTime, s_utcTimeZone, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, s_cachedData);
+ }
+ else
+ {
+ return ConvertTime(dateTime, FindSystemTimeZoneById(sourceTimeZoneId), FindSystemTimeZoneById(destinationTimeZoneId));
+ }
+ }
+
+ /// <summary>
+ /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone
+ /// </summary>
+ public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
+ {
+ if (destinationTimeZone == null)
+ {
+ throw new ArgumentNullException(nameof(destinationTimeZone));
+ }
+
+ // calculate the destination time zone offset
+ DateTime utcDateTime = dateTimeOffset.UtcDateTime;
+ TimeSpan destinationOffset = GetUtcOffsetFromUtc(utcDateTime, destinationTimeZone);
+
+ // check for overflow
+ long ticks = utcDateTime.Ticks + destinationOffset.Ticks;
+
+ return
+ ticks > DateTimeOffset.MaxValue.Ticks ? DateTimeOffset.MaxValue :
+ ticks < DateTimeOffset.MinValue.Ticks ? DateTimeOffset.MinValue :
+ new DateTimeOffset(ticks, destinationOffset);
+ }
+
+ /// <summary>
+ /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone
+ /// </summary>
+ public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo destinationTimeZone)
+ {
+ if (destinationTimeZone == null)
+ {
+ throw new ArgumentNullException(nameof(destinationTimeZone));
+ }
+
+ // Special case to give a way clearing the cache without exposing ClearCachedData()
+ if (dateTime.Ticks == 0)
+ {
+ ClearCachedData();
+ }
+ CachedData cachedData = s_cachedData;
+ TimeZoneInfo sourceTimeZone = dateTime.Kind == DateTimeKind.Utc ? s_utcTimeZone : cachedData.Local;
+ return ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, cachedData);
+ }
+
+ /// <summary>
+ /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone
+ /// </summary>
+ public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone) =>
+ ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, s_cachedData);
+
+ /// <summary>
+ /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone
+ /// </summary>
+ internal static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags) =>
+ ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, flags, s_cachedData);
+
+ private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags, CachedData cachedData)
+ {
+ if (sourceTimeZone == null)
+ {
+ throw new ArgumentNullException(nameof(sourceTimeZone));
+ }
+
+ if (destinationTimeZone == null)
+ {
+ throw new ArgumentNullException(nameof(destinationTimeZone));
+ }
+
+ DateTimeKind sourceKind = cachedData.GetCorrespondingKind(sourceTimeZone);
+ if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && (dateTime.Kind != DateTimeKind.Unspecified) && (dateTime.Kind != sourceKind))
+ {
+ throw new ArgumentException(SR.Argument_ConvertMismatch, nameof(sourceTimeZone));
+ }
+
+ //
+ // check to see if the DateTime is in an invalid time range. This check
+ // requires the current AdjustmentRule and DaylightTime - which are also
+ // needed to calculate 'sourceOffset' in the normal conversion case.
+ // By calculating the 'sourceOffset' here we improve the
+ // performance for the normal case at the expense of the 'ArgumentException'
+ // case and Loss-less Local special cases.
+ //
+ int? sourceRuleIndex;
+ AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime, out sourceRuleIndex);
+ TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset;
+
+ if (sourceRule != null)
+ {
+ sourceOffset = sourceOffset + sourceRule.BaseUtcOffsetDelta;
+ if (sourceRule.HasDaylightSaving)
+ {
+ bool sourceIsDaylightSavings = false;
+ DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule, sourceRuleIndex);
+
+ // 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
+ // period that supports DST
+ if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && GetIsInvalidTime(dateTime, sourceRule, sourceDaylightTime))
+ {
+ throw new ArgumentException(SR.Argument_DateTimeIsInvalid, nameof(dateTime));
+ }
+ sourceIsDaylightSavings = GetIsDaylightSavings(dateTime, sourceRule, sourceDaylightTime, flags);
+
+ // adjust the sourceOffset according to the Adjustment Rule / Daylight Saving Rule
+ sourceOffset += (sourceIsDaylightSavings ? sourceRule.DaylightDelta : TimeSpan.Zero /*FUTURE: sourceRule.StandardDelta*/);
+ }
+ }
+
+ DateTimeKind targetKind = cachedData.GetCorrespondingKind(destinationTimeZone);
+
+ // handle the special case of Loss-less Local->Local and UTC->UTC)
+ if (dateTime.Kind != DateTimeKind.Unspecified && sourceKind != DateTimeKind.Unspecified && sourceKind == targetKind)
+ {
+ return dateTime;
+ }
+
+ long utcTicks = dateTime.Ticks - sourceOffset.Ticks;
+
+ // handle the normal case by converting from 'source' to UTC and then to 'target'
+ bool isAmbiguousLocalDst;
+ DateTime targetConverted = ConvertUtcToTimeZone(utcTicks, destinationTimeZone, out isAmbiguousLocalDst);
+
+ if (targetKind == DateTimeKind.Local)
+ {
+ // Because the ticks conversion between UTC and local is lossy, we need to capture whether the
+ // time is in a repeated hour so that it can be passed to the DateTime constructor.
+ return new DateTime(targetConverted.Ticks, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+ else
+ {
+ return new DateTime(targetConverted.Ticks, targetKind);
+ }
+ }
+
+ /// <summary>
+ /// Converts the value of a DateTime object from Coordinated Universal Time (UTC) to the destinationTimeZone.
+ /// </summary>
+ public static DateTime ConvertTimeFromUtc(DateTime dateTime, TimeZoneInfo destinationTimeZone) =>
+ ConvertTime(dateTime, s_utcTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, s_cachedData);
+
+ /// <summary>
+ /// Converts the value of a DateTime object to Coordinated Universal Time (UTC).
+ /// </summary>
+ public static DateTime ConvertTimeToUtc(DateTime dateTime)
+ {
+ if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ return dateTime;
+ }
+ CachedData cachedData = s_cachedData;
+ return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, TimeZoneInfoOptions.None, cachedData);
+ }
+
+ /// <summary>
+ /// Converts the value of a DateTime object to Coordinated Universal Time (UTC).
+ /// </summary>
+ internal static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ return dateTime;
+ }
+ CachedData cachedData = s_cachedData;
+ return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags, cachedData);
+ }
+
+ /// <summary>
+ /// Converts the value of a DateTime object to Coordinated Universal Time (UTC).
+ /// </summary>
+ public static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfo sourceTimeZone) =>
+ ConvertTime(dateTime, sourceTimeZone, s_utcTimeZone, TimeZoneInfoOptions.None, s_cachedData);
+
+ /// <summary>
+ /// Returns value equality. Equals does not compare any localizable
+ /// String objects (DisplayName, StandardName, DaylightName).
+ /// </summary>
+ public bool Equals(TimeZoneInfo other) =>
+ other != null &&
+ string.Equals(_id, other._id, StringComparison.OrdinalIgnoreCase) &&
+ HasSameRules(other);
+
+ public override bool Equals(object obj) => Equals(obj as TimeZoneInfo);
+
+ public static TimeZoneInfo FromSerializedString(string source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (source.Length == 0)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidSerializedString, source), nameof(source));
+ }
+
+ return StringSerializer.GetDeserializedTimeZoneInfo(source);
+ }
+
+ public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(_id);
+
+ /// <summary>
+ /// Returns a ReadOnlyCollection<TimeZoneInfo> containing all valid TimeZone's
+ /// from the local machine. The entries in the collection are sorted by
+ /// <see cref="DisplayName"/>.
+ /// This method does *not* throw TimeZoneNotFoundException or InvalidTimeZoneException.
+ /// </summary>
+ public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones()
+ {
+ CachedData cachedData = s_cachedData;
+
+ lock (cachedData)
+ {
+ if (cachedData._readOnlySystemTimeZones == null)
+ {
+ PopulateAllSystemTimeZones(cachedData);
+ cachedData._allSystemTimeZonesRead = true;
+
+ List<TimeZoneInfo> list;
+ if (cachedData._systemTimeZones != null)
+ {
+ // return a collection of the cached system time zones
+ list = new List<TimeZoneInfo>(cachedData._systemTimeZones.Values);
+ }
+ else
+ {
+ // return an empty collection
+ list = new List<TimeZoneInfo>();
+ }
+
+ // sort and copy the TimeZoneInfo's into a ReadOnlyCollection for the user
+ list.Sort((x, y) =>
+ {
+ // sort by BaseUtcOffset first and by DisplayName second - this is similar to the Windows Date/Time control panel
+ int comparison = x.BaseUtcOffset.CompareTo(y.BaseUtcOffset);
+ return comparison == 0 ? string.CompareOrdinal(x.DisplayName, y.DisplayName) : comparison;
+ });
+
+ cachedData._readOnlySystemTimeZones = new ReadOnlyCollection<TimeZoneInfo>(list);
+ }
+ }
+ return cachedData._readOnlySystemTimeZones;
+ }
+
+ /// <summary>
+ /// Value equality on the "adjustmentRules" array
+ /// </summary>
+ public bool HasSameRules(TimeZoneInfo other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ // check the utcOffset and supportsDaylightSavingTime members
+ if (_baseUtcOffset != other._baseUtcOffset ||
+ _supportsDaylightSavingTime != other._supportsDaylightSavingTime)
+ {
+ return false;
+ }
+
+ bool sameRules;
+ AdjustmentRule[] currentRules = _adjustmentRules;
+ AdjustmentRule[] otherRules = other._adjustmentRules;
+
+ sameRules =
+ (currentRules == null && otherRules == null) ||
+ (currentRules != null && otherRules != null);
+
+ if (!sameRules)
+ {
+ // AdjustmentRule array mismatch
+ return false;
+ }
+
+ if (currentRules != null)
+ {
+ if (currentRules.Length != otherRules.Length)
+ {
+ // AdjustmentRule array length mismatch
+ return false;
+ }
+
+ for (int i = 0; i < currentRules.Length; i++)
+ {
+ if (!(currentRules[i]).Equals(otherRules[i]))
+ {
+ // AdjustmentRule value-equality mismatch
+ return false;
+ }
+ }
+ }
+ return sameRules;
+ }
+
+ /// <summary>
+ /// Returns a TimeZoneInfo instance that represents the local time on the machine.
+ /// Accessing this property may throw InvalidTimeZoneException or COMException
+ /// if the machine is in an unstable or corrupt state.
+ /// </summary>
+ public static TimeZoneInfo Local => s_cachedData.Local;
+
+ //
+ // ToSerializedString -
+ //
+ // "TimeZoneInfo" := TimeZoneInfo Data;[AdjustmentRule Data 1];...;[AdjustmentRule Data N]
+ //
+ // "TimeZoneInfo Data" := <_id>;<_baseUtcOffset>;<_displayName>;
+ // <_standardDisplayName>;<_daylightDispayName>;
+ //
+ // "AdjustmentRule Data" := <DateStart>;<DateEnd>;<DaylightDelta>;
+ // [TransitionTime Data DST Start]
+ // [TransitionTime Data DST End]
+ //
+ // "TransitionTime Data" += <DaylightStartTimeOfDat>;<Month>;<Week>;<DayOfWeek>;<Day>
+ //
+ public string ToSerializedString() => StringSerializer.GetSerializedString(this);
+
+ /// <summary>
+ /// Returns the <see cref="DisplayName"/>: "(GMT-08:00) Pacific Time (US &amp; Canada); Tijuana"
+ /// </summary>
+ public override string ToString() => DisplayName;
+
+ /// <summary>
+ /// Returns a TimeZoneInfo instance that represents Universal Coordinated Time (UTC)
+ /// </summary>
+ public static TimeZoneInfo Utc => s_utcTimeZone;
+
+ private TimeZoneInfo(
+ string id,
+ TimeSpan baseUtcOffset,
+ string displayName,
+ string standardDisplayName,
+ string daylightDisplayName,
+ AdjustmentRule[] adjustmentRules,
+ bool disableDaylightSavingTime)
+ {
+ bool adjustmentRulesSupportDst;
+ ValidateTimeZoneInfo(id, baseUtcOffset, adjustmentRules, out adjustmentRulesSupportDst);
+
+ _id = id;
+ _baseUtcOffset = baseUtcOffset;
+ _displayName = displayName;
+ _standardDisplayName = standardDisplayName;
+ _daylightDisplayName = disableDaylightSavingTime ? null : daylightDisplayName;
+ _supportsDaylightSavingTime = adjustmentRulesSupportDst && !disableDaylightSavingTime;
+ _adjustmentRules = adjustmentRules;
+ }
+
+ /// <summary>
+ /// Returns a simple TimeZoneInfo instance that does not support Daylight Saving Time.
+ /// </summary>
+ public static TimeZoneInfo CreateCustomTimeZone(
+ string id,
+ TimeSpan baseUtcOffset,
+ string displayName,
+ string standardDisplayName)
+ {
+ return new TimeZoneInfo(
+ id,
+ baseUtcOffset,
+ displayName,
+ standardDisplayName,
+ standardDisplayName,
+ adjustmentRules: null,
+ disableDaylightSavingTime: false);
+ }
+
+ /// <summary>
+ /// Returns a TimeZoneInfo instance that may support Daylight Saving Time.
+ /// </summary>
+ public static TimeZoneInfo CreateCustomTimeZone(
+ string id,
+ TimeSpan baseUtcOffset,
+ string displayName,
+ string standardDisplayName,
+ string daylightDisplayName,
+ AdjustmentRule[] adjustmentRules)
+ {
+ return CreateCustomTimeZone(
+ id,
+ baseUtcOffset,
+ displayName,
+ standardDisplayName,
+ daylightDisplayName,
+ adjustmentRules,
+ disableDaylightSavingTime: false);
+ }
+
+ /// <summary>
+ /// Returns a TimeZoneInfo instance that may support Daylight Saving Time.
+ /// </summary>
+ public static TimeZoneInfo CreateCustomTimeZone(
+ string id,
+ TimeSpan baseUtcOffset,
+ string displayName,
+ string standardDisplayName,
+ string daylightDisplayName,
+ AdjustmentRule[] adjustmentRules,
+ bool disableDaylightSavingTime)
+ {
+ if (!disableDaylightSavingTime && adjustmentRules?.Length > 0)
+ {
+ adjustmentRules = (AdjustmentRule[])adjustmentRules.Clone();
+ }
+
+ return new TimeZoneInfo(
+ id,
+ baseUtcOffset,
+ displayName,
+ standardDisplayName,
+ daylightDisplayName,
+ adjustmentRules,
+ disableDaylightSavingTime);
+ }
+
+ void IDeserializationCallback.OnDeserialization(object sender)
+ {
+ try
+ {
+ bool adjustmentRulesSupportDst;
+ ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out adjustmentRulesSupportDst);
+
+ if (adjustmentRulesSupportDst != _supportsDaylightSavingTime)
+ {
+ throw new SerializationException(SR.Format(SR.Serialization_CorruptField, "SupportsDaylightSavingTime"));
+ }
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ catch (InvalidTimeZoneException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ info.AddValue("Id", _id); // Do not rename (binary serialization)
+ info.AddValue("DisplayName", _displayName); // Do not rename (binary serialization)
+ info.AddValue("StandardName", _standardDisplayName); // Do not rename (binary serialization)
+ info.AddValue("DaylightName", _daylightDisplayName); // Do not rename (binary serialization)
+ info.AddValue("BaseUtcOffset", _baseUtcOffset); // Do not rename (binary serialization)
+ info.AddValue("AdjustmentRules", _adjustmentRules); // Do not rename (binary serialization)
+ info.AddValue("SupportsDaylightSavingTime", _supportsDaylightSavingTime); // Do not rename (binary serialization)
+ }
+
+ private TimeZoneInfo(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ _id = (string)info.GetValue("Id", typeof(string)); // Do not rename (binary serialization)
+ _displayName = (string)info.GetValue("DisplayName", typeof(string)); // Do not rename (binary serialization)
+ _standardDisplayName = (string)info.GetValue("StandardName", typeof(string)); // Do not rename (binary serialization)
+ _daylightDisplayName = (string)info.GetValue("DaylightName", typeof(string)); // Do not rename (binary serialization)
+ _baseUtcOffset = (TimeSpan)info.GetValue("BaseUtcOffset", typeof(TimeSpan)); // Do not rename (binary serialization)
+ _adjustmentRules = (AdjustmentRule[])info.GetValue("AdjustmentRules", typeof(AdjustmentRule[])); // Do not rename (binary serialization)
+ _supportsDaylightSavingTime = (bool)info.GetValue("SupportsDaylightSavingTime", typeof(bool)); // Do not rename (binary serialization)
+ }
+
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, out int? ruleIndex)
+ {
+ AdjustmentRule result = GetAdjustmentRuleForTime(dateTime, dateTimeisUtc: false, ruleIndex: out ruleIndex);
+ Debug.Assert(result == null || ruleIndex.HasValue, "If an AdjustmentRule was found, ruleIndex should also be set.");
+
+ return result;
+ }
+
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc, out int? ruleIndex)
+ {
+ if (_adjustmentRules == null || _adjustmentRules.Length == 0)
+ {
+ ruleIndex = null;
+ return null;
+ }
+
+ // Only check the whole-date portion of the dateTime for DateTimeKind.Unspecified rules -
+ // This is because the AdjustmentRule DateStart & DateEnd are stored as
+ // Date-only values {4/2/2006 - 10/28/2006} but actually represent the
+ // time span {4/2/2006@00:00:00.00000 - 10/28/2006@23:59:59.99999}
+ DateTime date = dateTimeisUtc ?
+ (dateTime + BaseUtcOffset).Date :
+ dateTime.Date;
+
+ int low = 0;
+ int high = _adjustmentRules.Length - 1;
+
+ while (low <= high)
+ {
+ int median = low + ((high - low) >> 1);
+
+ AdjustmentRule rule = _adjustmentRules[median];
+ AdjustmentRule previousRule = median > 0 ? _adjustmentRules[median - 1] : rule;
+
+ int compareResult = CompareAdjustmentRuleToDateTime(rule, previousRule, dateTime, date, dateTimeisUtc);
+ if (compareResult == 0)
+ {
+ ruleIndex = median;
+ return rule;
+ }
+ else if (compareResult < 0)
+ {
+ low = median + 1;
+ }
+ else
+ {
+ high = median - 1;
+ }
+ }
+
+ ruleIndex = null;
+ return null;
+ }
+
+ /// <summary>
+ /// Determines if 'rule' is the correct AdjustmentRule for the given dateTime.
+ /// </summary>
+ /// <returns>
+ /// A value less than zero if rule is for times before dateTime.
+ /// Zero if rule is correct for dateTime.
+ /// A value greater than zero if rule is for times after dateTime.
+ /// </returns>
+ private int CompareAdjustmentRuleToDateTime(AdjustmentRule rule, AdjustmentRule previousRule,
+ DateTime dateTime, DateTime dateOnly, bool dateTimeisUtc)
+ {
+ bool isAfterStart;
+ if (rule.DateStart.Kind == DateTimeKind.Utc)
+ {
+ DateTime dateTimeToCompare = dateTimeisUtc ?
+ dateTime :
+ // use the previous rule to compute the dateTimeToCompare, since the time daylight savings "switches"
+ // is based on the previous rule's offset
+ ConvertToUtc(dateTime, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta);
+
+ isAfterStart = dateTimeToCompare >= rule.DateStart;
+ }
+ else
+ {
+ // if the rule's DateStart is Unspecified, then use the whole-date portion
+ isAfterStart = dateOnly >= rule.DateStart;
+ }
+
+ if (!isAfterStart)
+ {
+ return 1;
+ }
+
+ bool isBeforeEnd;
+ if (rule.DateEnd.Kind == DateTimeKind.Utc)
+ {
+ DateTime dateTimeToCompare = dateTimeisUtc ?
+ dateTime :
+ ConvertToUtc(dateTime, rule.DaylightDelta, rule.BaseUtcOffsetDelta);
+
+ isBeforeEnd = dateTimeToCompare <= rule.DateEnd;
+ }
+ else
+ {
+ // if the rule's DateEnd is Unspecified, then use the whole-date portion
+ isBeforeEnd = dateOnly <= rule.DateEnd;
+ }
+
+ return isBeforeEnd ? 0 : -1;
+ }
+
+ /// <summary>
+ /// Converts the dateTime to UTC using the specified deltas.
+ /// </summary>
+ private DateTime ConvertToUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta) =>
+ ConvertToFromUtc(dateTime, daylightDelta, baseUtcOffsetDelta, convertToUtc: true);
+
+ /// <summary>
+ /// Converts the dateTime from UTC using the specified deltas.
+ /// </summary>
+ private DateTime ConvertFromUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta) =>
+ ConvertToFromUtc(dateTime, daylightDelta, baseUtcOffsetDelta, convertToUtc: false);
+
+ /// <summary>
+ /// Converts the dateTime to or from UTC using the specified deltas.
+ /// </summary>
+ private DateTime ConvertToFromUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta, bool convertToUtc)
+ {
+ TimeSpan offset = BaseUtcOffset + daylightDelta + baseUtcOffsetDelta;
+ if (convertToUtc)
+ {
+ offset = offset.Negate();
+ }
+
+ long ticks = dateTime.Ticks + offset.Ticks;
+
+ return
+ ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue :
+ ticks < DateTime.MinValue.Ticks ? DateTime.MinValue :
+ new DateTime(ticks);
+ }
+
+ /// <summary>
+ /// Helper function that converts a dateTime from UTC into the destinationTimeZone
+ /// - Returns DateTime.MaxValue when the converted value is too large.
+ /// - Returns DateTime.MinValue when the converted value is too small.
+ /// </summary>
+ private static DateTime ConvertUtcToTimeZone(long ticks, TimeZoneInfo destinationTimeZone, out bool isAmbiguousLocalDst)
+ {
+ // used to calculate the UTC offset in the destinationTimeZone
+ DateTime utcConverted =
+ ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue :
+ ticks < DateTime.MinValue.Ticks ? DateTime.MinValue :
+ new DateTime(ticks);
+
+ // verify the time is between MinValue and MaxValue in the new time zone
+ TimeSpan offset = GetUtcOffsetFromUtc(utcConverted, destinationTimeZone, out isAmbiguousLocalDst);
+ ticks += offset.Ticks;
+
+ return
+ ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue :
+ ticks < DateTime.MinValue.Ticks ? DateTime.MinValue :
+ new DateTime(ticks);
+ }
+
+ /// <summary>
+ /// Helper function that returns a DaylightTime from a year and AdjustmentRule.
+ /// </summary>
+ private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule, int? ruleIndex)
+ {
+ TimeSpan delta = rule.DaylightDelta;
+ DateTime startTime;
+ DateTime endTime;
+ if (rule.NoDaylightTransitions)
+ {
+ // NoDaylightTransitions rules don't use DaylightTransition Start and End, instead
+ // the DateStart and DateEnd are UTC times that represent when daylight savings time changes.
+ // Convert the UTC times into adjusted time zone times.
+
+ // use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
+ startTime = ConvertFromUtc(rule.DateStart, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta);
+
+ endTime = ConvertFromUtc(rule.DateEnd, rule.DaylightDelta, rule.BaseUtcOffsetDelta);
+ }
+ else
+ {
+ startTime = TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
+ endTime = TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
+ }
+ return new DaylightTimeStruct(startTime, endTime, delta);
+ }
+
+ /// <summary>
+ /// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
+ /// This function assumes the dateTime and AdjustmentRule are both in the same time zone.
+ /// </summary>
+ private static bool GetIsDaylightSavings(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime, TimeZoneInfoOptions flags)
+ {
+ if (rule == null)
+ {
+ return false;
+ }
+
+ DateTime startTime;
+ DateTime endTime;
+
+ if (time.Kind == DateTimeKind.Local)
+ {
+ // startTime and endTime represent the period from either the start of
+ // DST to the end and ***includes*** the potentially overlapped times
+ startTime = rule.IsStartDateMarkerForBeginningOfYear() ?
+ new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) :
+ daylightTime.Start + daylightTime.Delta;
+
+ endTime = rule.IsEndDateMarkerForEndOfYear() ?
+ new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) :
+ daylightTime.End;
+ }
+ else
+ {
+ // startTime and endTime represent the period from either the start of DST to the end and
+ // ***does not include*** the potentially overlapped times
+ //
+ // -=-=-=-=-=- Pacific Standard Time -=-=-=-=-=-=-
+ // April 2, 2006 October 29, 2006
+ // 2AM 3AM 1AM 2AM
+ // | +1 hr | | -1 hr |
+ // | <invalid time> | | <ambiguous time> |
+ // [========== DST ========>)
+ //
+ // -=-=-=-=-=- Some Weird Time Zone -=-=-=-=-=-=-
+ // April 2, 2006 October 29, 2006
+ // 1AM 2AM 2AM 3AM
+ // | -1 hr | | +1 hr |
+ // | <ambiguous time> | | <invalid time> |
+ // [======== DST ========>)
+ //
+ bool invalidAtStart = rule.DaylightDelta > TimeSpan.Zero;
+
+ startTime = rule.IsStartDateMarkerForBeginningOfYear() ?
+ new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) :
+ daylightTime.Start + (invalidAtStart ? rule.DaylightDelta : TimeSpan.Zero); /* FUTURE: - rule.StandardDelta; */
+
+ endTime = rule.IsEndDateMarkerForEndOfYear() ?
+ new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) :
+ daylightTime.End + (invalidAtStart ? -rule.DaylightDelta : TimeSpan.Zero);
+ }
+
+ bool isDst = CheckIsDst(startTime, time, endTime, false, rule);
+
+ // If this date was previously converted from a UTC date and we were able to detect that the local
+ // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity.
+ if (isDst && time.Kind == DateTimeKind.Local)
+ {
+ // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the
+ // clock back. It is theoretically possible to have a positive delta, (which would really be daylight
+ // reduction time), where you would have to wind the clock back in the begnning.
+ if (GetIsAmbiguousTime(time, rule, daylightTime))
+ {
+ isDst = time.IsAmbiguousDaylightSavingTime();
+ }
+ }
+
+ return isDst;
+ }
+
+ /// <summary>
+ /// Gets the offset that should be used to calculate DST start times from a UTC time.
+ /// </summary>
+ private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule, int? ruleIndex)
+ {
+ if (rule.NoDaylightTransitions)
+ {
+ // use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
+ return baseUtcOffset + previousRule.BaseUtcOffsetDelta + previousRule.DaylightDelta;
+ }
+ else
+ {
+ return baseUtcOffset + rule.BaseUtcOffsetDelta; /* FUTURE: + rule.StandardDelta; */
+ }
+ }
+
+ /// <summary>
+ /// Gets the offset that should be used to calculate DST end times from a UTC time.
+ /// </summary>
+ private TimeSpan GetDaylightSavingsEndOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule)
+ {
+ // NOTE: even NoDaylightTransitions rules use this logic since DST ends w.r.t. the current rule
+ return baseUtcOffset + rule.BaseUtcOffsetDelta + rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */
+ }
+
+ /// <summary>
+ /// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
+ /// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone.
+ /// </summary>
+ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, int? ruleIndex, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
+ {
+ isAmbiguousLocalDst = false;
+
+ if (rule == null)
+ {
+ return false;
+ }
+
+ // Get the daylight changes for the year of the specified time.
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule, ruleIndex);
+
+ // The start and end times represent the range of universal times that are in DST for that year.
+ // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
+ // the unusual case of a negative daylight savings delta.
+ // We need to handle the case if the current rule has daylight saving end by the end of year. If so, we need to check if next year starts with daylight saving on
+ // and get the actual daylight saving end time. Here is example for such case:
+ // Converting the UTC datetime "12/31/2011 8:00:00 PM" to "(UTC+03:00) Moscow, St. Petersburg, Volgograd (RTZ 2)" zone.
+ // In 2011 the daylight saving will go through the end of the year. If we use the end of 2011 as the daylight saving end,
+ // that will fail the conversion because the UTC time +4 hours (3 hours for the zone UTC offset and 1 hour for daylight saving) will move us to the next year "1/1/2012 12:00 AM",
+ // checking against the end of 2011 will tell we are not in daylight saving which is wrong and the conversion will be off by one hour.
+ // Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
+
+ bool ignoreYearAdjustment = false;
+ TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule, ruleIndex);
+ DateTime startTime;
+ if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year)
+ {
+ int? previousYearRuleIndex;
+ AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.Start.Year - 1, 12, 31),
+ out previousYearRuleIndex);
+ if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear())
+ {
+ DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(
+ daylightTime.Start.Year - 1,
+ previousYearRule,
+ previousYearRuleIndex);
+ startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
+ ignoreYearAdjustment = true;
+ }
+ else
+ {
+ startTime = new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) - dstStartOffset;
+ }
+ }
+ else
+ {
+ startTime = daylightTime.Start - dstStartOffset;
+ }
+
+ TimeSpan dstEndOffset = zone.GetDaylightSavingsEndOffsetFromUtc(utc, rule);
+ DateTime endTime;
+ if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year)
+ {
+ int? nextYearRuleIndex;
+ AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.End.Year + 1, 1, 1),
+ out nextYearRuleIndex);
+ if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear())
+ {
+ if (nextYearRule.IsEndDateMarkerForEndOfYear())
+ {
+ // next year end with daylight saving on too
+ endTime = new DateTime(daylightTime.End.Year + 1, 12, 31) - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
+ }
+ else
+ {
+ DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(
+ daylightTime.End.Year + 1,
+ nextYearRule,
+ nextYearRuleIndex);
+ endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
+ }
+ ignoreYearAdjustment = true;
+ }
+ else
+ {
+ endTime = new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) - dstEndOffset;
+ }
+ }
+ else
+ {
+ endTime = daylightTime.End - dstEndOffset;
+ }
+
+ DateTime ambiguousStart;
+ DateTime ambiguousEnd;
+ if (daylightTime.Delta.Ticks > 0)
+ {
+ ambiguousStart = endTime - daylightTime.Delta;
+ ambiguousEnd = endTime;
+ }
+ else
+ {
+ ambiguousStart = startTime;
+ ambiguousEnd = startTime - daylightTime.Delta;
+ }
+
+ bool isDst = CheckIsDst(startTime, time, endTime, ignoreYearAdjustment, rule);
+
+ // See if the resulting local time becomes ambiguous. This must be captured here or the
+ // DateTime will not be able to round-trip back to UTC accurately.
+ if (isDst)
+ {
+ isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
+
+ if (!isAmbiguousLocalDst && ambiguousStart.Year != ambiguousEnd.Year)
+ {
+ // there exists an extreme corner case where the start or end period is on a year boundary and
+ // because of this the comparison above might have been performed for a year-early or a year-later
+ // than it should have been.
+ DateTime ambiguousStartModified;
+ DateTime ambiguousEndModified;
+ try
+ {
+ ambiguousStartModified = ambiguousStart.AddYears(1);
+ ambiguousEndModified = ambiguousEnd.AddYears(1);
+ isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
+ }
+ catch (ArgumentOutOfRangeException) { }
+
+ if (!isAmbiguousLocalDst)
+ {
+ try
+ {
+ ambiguousStartModified = ambiguousStart.AddYears(-1);
+ ambiguousEndModified = ambiguousEnd.AddYears(-1);
+ isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
+ }
+ catch (ArgumentOutOfRangeException) { }
+ }
+ }
+ }
+
+ return isDst;
+ }
+
+ private static bool CheckIsDst(DateTime startTime, DateTime time, DateTime endTime, bool ignoreYearAdjustment, AdjustmentRule rule)
+ {
+ // NoDaylightTransitions AdjustmentRules should never get their year adjusted since they adjust the offset for the
+ // entire time period - which may be for multiple years
+ if (!ignoreYearAdjustment && !rule.NoDaylightTransitions)
+ {
+ int startTimeYear = startTime.Year;
+ int endTimeYear = endTime.Year;
+
+ if (startTimeYear != endTimeYear)
+ {
+ endTime = endTime.AddYears(startTimeYear - endTimeYear);
+ }
+
+ int timeYear = time.Year;
+
+ if (startTimeYear != timeYear)
+ {
+ time = time.AddYears(startTimeYear - timeYear);
+ }
+ }
+
+ if (startTime > endTime)
+ {
+ // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
+ // Note, the summer in the southern hemisphere begins late in the year.
+ return (time < endTime || time >= startTime);
+ }
+ else if (rule.NoDaylightTransitions)
+ {
+ // In NoDaylightTransitions AdjustmentRules, the startTime is always before the endTime,
+ // and both the start and end times are inclusive
+ return time >= startTime && time <= endTime;
+ }
+ else
+ {
+ // In northern hemisphere, the daylight saving time starts in the middle of the year.
+ return time >= startTime && time < endTime;
+ }
+ }
+
+ /// <summary>
+ /// Returns true when the dateTime falls into an ambiguous time range.
+ ///
+ /// For example, in Pacific Standard Time on Sunday, October 29, 2006 time jumps from
+ /// 2AM to 1AM. This means the timeline on Sunday proceeds as follows:
+ /// 12AM ... [1AM ... 1:59:59AM -> 1AM ... 1:59:59AM] 2AM ... 3AM ...
+ ///
+ /// In this example, any DateTime values that fall into the [1AM - 1:59:59AM] range
+ /// are ambiguous; as it is unclear if these times are in Daylight Saving Time.
+ /// </summary>
+ private static bool GetIsAmbiguousTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime)
+ {
+ bool isAmbiguous = false;
+ if (rule == null || rule.DaylightDelta == TimeSpan.Zero)
+ {
+ return isAmbiguous;
+ }
+
+ DateTime startAmbiguousTime;
+ DateTime endAmbiguousTime;
+
+ // if at DST start we transition forward in time then there is an ambiguous time range at the DST end
+ if (rule.DaylightDelta > TimeSpan.Zero)
+ {
+ if (rule.IsEndDateMarkerForEndOfYear())
+ { // year end with daylight on so there is no ambiguous time
+ return false;
+ }
+ startAmbiguousTime = daylightTime.End;
+ endAmbiguousTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */
+ }
+ else
+ {
+ if (rule.IsStartDateMarkerForBeginningOfYear())
+ { // year start with daylight on so there is no ambiguous time
+ return false;
+ }
+ startAmbiguousTime = daylightTime.Start;
+ endAmbiguousTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */
+ }
+
+ isAmbiguous = (time >= endAmbiguousTime && time < startAmbiguousTime);
+
+ if (!isAmbiguous && startAmbiguousTime.Year != endAmbiguousTime.Year)
+ {
+ // there exists an extreme corner case where the start or end period is on a year boundary and
+ // because of this the comparison above might have been performed for a year-early or a year-later
+ // than it should have been.
+ DateTime startModifiedAmbiguousTime;
+ DateTime endModifiedAmbiguousTime;
+ try
+ {
+ startModifiedAmbiguousTime = startAmbiguousTime.AddYears(1);
+ endModifiedAmbiguousTime = endAmbiguousTime.AddYears(1);
+ isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime);
+ }
+ catch (ArgumentOutOfRangeException) { }
+
+ if (!isAmbiguous)
+ {
+ try
+ {
+ startModifiedAmbiguousTime = startAmbiguousTime.AddYears(-1);
+ endModifiedAmbiguousTime = endAmbiguousTime.AddYears(-1);
+ isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime);
+ }
+ catch (ArgumentOutOfRangeException) { }
+ }
+ }
+ return isAmbiguous;
+ }
+
+ /// <summary>
+ /// Helper function that checks if a given DateTime is in an invalid time ("time hole")
+ /// A "time hole" occurs at a DST transition point when time jumps forward;
+ /// For example, in Pacific Standard Time on Sunday, April 2, 2006 time jumps from
+ /// 1:59:59.9999999 to 3AM. The time range 2AM to 2:59:59.9999999AM is the "time hole".
+ /// A "time hole" is not limited to only occurring at the start of DST, and may occur at
+ /// the end of DST as well.
+ /// </summary>
+ private static bool GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime)
+ {
+ bool isInvalid = false;
+ if (rule == null || rule.DaylightDelta == TimeSpan.Zero)
+ {
+ return isInvalid;
+ }
+
+ DateTime startInvalidTime;
+ DateTime endInvalidTime;
+
+ // if at DST start we transition forward in time then there is an ambiguous time range at the DST end
+ if (rule.DaylightDelta < TimeSpan.Zero)
+ {
+ // if the year ends with daylight saving on then there cannot be any time-hole's in that year.
+ if (rule.IsEndDateMarkerForEndOfYear())
+ return false;
+
+ startInvalidTime = daylightTime.End;
+ endInvalidTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */
+ }
+ else
+ {
+ // if the year starts with daylight saving on then there cannot be any time-hole's in that year.
+ if (rule.IsStartDateMarkerForBeginningOfYear())
+ return false;
+
+ startInvalidTime = daylightTime.Start;
+ endInvalidTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */
+ }
+
+ isInvalid = (time >= startInvalidTime && time < endInvalidTime);
+
+ if (!isInvalid && startInvalidTime.Year != endInvalidTime.Year)
+ {
+ // there exists an extreme corner case where the start or end period is on a year boundary and
+ // because of this the comparison above might have been performed for a year-early or a year-later
+ // than it should have been.
+ DateTime startModifiedInvalidTime;
+ DateTime endModifiedInvalidTime;
+ try
+ {
+ startModifiedInvalidTime = startInvalidTime.AddYears(1);
+ endModifiedInvalidTime = endInvalidTime.AddYears(1);
+ isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime);
+ }
+ catch (ArgumentOutOfRangeException) { }
+
+ if (!isInvalid)
+ {
+ try
+ {
+ startModifiedInvalidTime = startInvalidTime.AddYears(-1);
+ endModifiedInvalidTime = endInvalidTime.AddYears(-1);
+ isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime);
+ }
+ catch (ArgumentOutOfRangeException) { }
+ }
+ }
+ return isInvalid;
+ }
+
+ /// <summary>
+ /// Helper function that calculates the UTC offset for a dateTime in a timeZone.
+ /// This function assumes that the dateTime is already converted into the timeZone.
+ /// </summary>
+ private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags)
+ {
+ TimeSpan baseOffset = zone.BaseUtcOffset;
+ int? ruleIndex;
+ AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time, out ruleIndex);
+
+ if (rule != null)
+ {
+ baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
+ if (rule.HasDaylightSaving)
+ {
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule, ruleIndex);
+ bool isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
+ baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
+ }
+ }
+
+ return baseOffset;
+ }
+
+ /// <summary>
+ /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone.
+ /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone.
+ /// </summary>
+ private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone)
+ {
+ bool isDaylightSavings;
+ return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings);
+ }
+
+ /// <summary>
+ /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone.
+ /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone.
+ /// </summary>
+ private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out bool isDaylightSavings)
+ {
+ bool isAmbiguousLocalDst;
+ return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings, out isAmbiguousLocalDst);
+ }
+
+ /// <summary>
+ /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone.
+ /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone.
+ /// </summary>
+ internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out bool isDaylightSavings, out bool isAmbiguousLocalDst)
+ {
+ isDaylightSavings = false;
+ isAmbiguousLocalDst = false;
+ TimeSpan baseOffset = zone.BaseUtcOffset;
+ int year;
+ int? ruleIndex;
+ AdjustmentRule rule;
+
+ if (time > s_maxDateOnly)
+ {
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue, out ruleIndex);
+ year = 9999;
+ }
+ else if (time < s_minDateOnly)
+ {
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue, out ruleIndex);
+ year = 1;
+ }
+ else
+ {
+ rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true, ruleIndex: out ruleIndex);
+ Debug.Assert(rule == null || ruleIndex.HasValue,
+ "If GetAdjustmentRuleForTime returned an AdjustmentRule, ruleIndex should also be set.");
+
+ // As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
+ // sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
+ // Libya Standard Time when used with the date 2011-12-31T23:59:59.9999999Z
+ // "W. Australia Standard Time" used with date 2005-12-31T23:59:00.0000000Z
+ DateTime targetTime = time + baseOffset;
+ year = targetTime.Year;
+ }
+
+ if (rule != null)
+ {
+ baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
+ if (rule.HasDaylightSaving)
+ {
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, ruleIndex, out isAmbiguousLocalDst, zone);
+ baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
+ }
+ }
+
+ return baseOffset;
+ }
+
+ /// <summary>
+ /// Helper function that converts a year and TransitionTime into a DateTime.
+ /// </summary>
+ internal static DateTime TransitionTimeToDateTime(int year, TransitionTime transitionTime)
+ {
+ DateTime value;
+ DateTime timeOfDay = transitionTime.TimeOfDay;
+
+ if (transitionTime.IsFixedDateRule)
+ {
+ // create a DateTime from the passed in year and the properties on the transitionTime
+
+ // if the day is out of range for the month then use the last day of the month
+ int day = DateTime.DaysInMonth(year, transitionTime.Month);
+
+ value = new DateTime(year, transitionTime.Month, (day < transitionTime.Day) ? day : transitionTime.Day,
+ timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
+ }
+ else
+ {
+ if (transitionTime.Week <= 4)
+ {
+ //
+ // Get the (transitionTime.Week)th Sunday.
+ //
+ value = new DateTime(year, transitionTime.Month, 1,
+ timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
+
+ int dayOfWeek = (int)value.DayOfWeek;
+ int delta = (int)transitionTime.DayOfWeek - dayOfWeek;
+ if (delta < 0)
+ {
+ delta += 7;
+ }
+ delta += 7 * (transitionTime.Week - 1);
+
+ if (delta > 0)
+ {
+ value = value.AddDays(delta);
+ }
+ }
+ else
+ {
+ //
+ // If TransitionWeek is greater than 4, we will get the last week.
+ //
+ int daysInMonth = DateTime.DaysInMonth(year, transitionTime.Month);
+ value = new DateTime(year, transitionTime.Month, daysInMonth,
+ timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
+
+ // This is the day of week for the last day of the month.
+ int dayOfWeek = (int)value.DayOfWeek;
+ int delta = dayOfWeek - (int)transitionTime.DayOfWeek;
+ if (delta < 0)
+ {
+ delta += 7;
+ }
+
+ if (delta > 0)
+ {
+ value = value.AddDays(-delta);
+ }
+ }
+ }
+ return value;
+ }
+
+ /// <summary>
+ /// Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
+ ///
+ /// This function may return null.
+ ///
+ /// assumes cachedData lock is taken
+ /// </summary>
+ private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData, bool alwaysFallbackToLocalMachine = false)
+ {
+ Debug.Assert(Monitor.IsEntered(cachedData));
+
+ TimeZoneInfoResult result = TimeZoneInfoResult.Success;
+ e = null;
+ TimeZoneInfo match = null;
+
+ // check the cache
+ if (cachedData._systemTimeZones != null)
+ {
+ if (cachedData._systemTimeZones.TryGetValue(id, out match))
+ {
+ if (dstDisabled && match._supportsDaylightSavingTime)
+ {
+ // we found a cache hit but we want a time zone without DST and this one has DST data
+ value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
+ }
+ else
+ {
+ value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
+ match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false);
+ }
+ return result;
+ }
+ }
+
+ // Fall back to reading from the local machine when the cache is not fully populated.
+ // On UNIX, there may be some tzfiles that aren't in the zones.tab file, and thus aren't returned from GetSystemTimeZones().
+ // If a caller asks for one of these zones before calling GetSystemTimeZones(), the time zone is returned successfully. But if
+ // GetSystemTimeZones() is called first, FindSystemTimeZoneById will throw TimeZoneNotFoundException, which is inconsistent.
+ // To fix this, when 'alwaysFallbackToLocalMachine' is true, even if _allSystemTimeZonesRead is true, try reading the tzfile
+ // from disk, but don't add the time zone to the list returned from GetSystemTimeZones(). These time zones will only be
+ // available if asked for directly.
+ if (!cachedData._allSystemTimeZonesRead || alwaysFallbackToLocalMachine)
+ {
+ result = TryGetTimeZoneFromLocalMachine(id, dstDisabled, out value, out e, cachedData);
+ }
+ else
+ {
+ result = TimeZoneInfoResult.TimeZoneNotFoundException;
+ value = null;
+ }
+
+ return result;
+ }
+
+ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, bool dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData)
+ {
+ TimeZoneInfoResult result;
+ TimeZoneInfo match;
+
+ result = TryGetTimeZoneFromLocalMachine(id, out match, out e);
+
+ if (result == TimeZoneInfoResult.Success)
+ {
+ if (cachedData._systemTimeZones == null)
+ cachedData._systemTimeZones = new Dictionary<string, TimeZoneInfo>(StringComparer.OrdinalIgnoreCase);
+
+ cachedData._systemTimeZones.Add(id, match);
+
+ if (dstDisabled && match._supportsDaylightSavingTime)
+ {
+ // we found a cache hit but we want a time zone without DST and this one has DST data
+ value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
+ }
+ else
+ {
+ value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
+ match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false);
+ }
+ }
+ else
+ {
+ value = null;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Helper function that validates the TimeSpan is within +/- 14.0 hours
+ /// </summary>
+ internal static bool UtcOffsetOutOfRange(TimeSpan offset) =>
+ offset.TotalHours < -14.0 || offset.TotalHours > 14.0;
+
+ /// <summary>
+ /// Helper function that performs all of the validation checks for the
+ /// factory methods and deserialization callback.
+ /// </summary>
+ private static void ValidateTimeZoneInfo(string id, TimeSpan baseUtcOffset, AdjustmentRule[] adjustmentRules, out bool adjustmentRulesSupportDst)
+ {
+ if (id == null)
+ {
+ throw new ArgumentNullException(nameof(id));
+ }
+
+ if (id.Length == 0)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidId, id), nameof(id));
+ }
+
+ if (UtcOffsetOutOfRange(baseUtcOffset))
+ {
+ throw new ArgumentOutOfRangeException(nameof(baseUtcOffset), SR.ArgumentOutOfRange_UtcOffset);
+ }
+
+ if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(baseUtcOffset));
+ }
+
+ adjustmentRulesSupportDst = false;
+
+ //
+ // "adjustmentRules" can either be null or a valid array of AdjustmentRule objects.
+ // A valid array is one that does not contain any null elements and all elements
+ // are sorted in chronological order
+ //
+
+ if (adjustmentRules != null && adjustmentRules.Length != 0)
+ {
+ adjustmentRulesSupportDst = true;
+ AdjustmentRule prev = null;
+ AdjustmentRule current = null;
+ for (int i = 0; i < adjustmentRules.Length; i++)
+ {
+ prev = current;
+ current = adjustmentRules[i];
+
+ if (current == null)
+ {
+ throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls);
+ }
+
+ // FUTURE: check to see if this rule supports Daylight Saving Time
+ // adjustmentRulesSupportDst = adjustmentRulesSupportDst || current.SupportsDaylightSavingTime;
+ // FUTURE: test baseUtcOffset + current.StandardDelta
+
+ if (UtcOffsetOutOfRange(baseUtcOffset + current.DaylightDelta))
+ {
+ throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta);
+ }
+
+ if (prev != null && current.DateStart <= prev.DateEnd)
+ {
+ // verify the rules are in chronological order and the DateStart/DateEnd do not overlap
+ throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesOutOfOrder);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Version.cs b/src/System.Private.CoreLib/shared/System/Version.cs
index df16be2cd..9e4cefcd6 100644
--- a/src/System.Private.CoreLib/shared/System/Version.cs
+++ b/src/System.Private.CoreLib/shared/System/Version.cs
@@ -300,7 +300,7 @@ namespace System
throw new ArgumentNullException(nameof(input));
}
- return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true);
+ return ParseVersion(input.AsSpan(), throwOnFailure: true);
}
public static Version Parse(ReadOnlySpan<char> input) =>
@@ -314,7 +314,7 @@ namespace System
return false;
}
- return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null;
+ return (result = ParseVersion(input.AsSpan(), throwOnFailure: false)) != null;
}
public static bool TryParse(ReadOnlySpan<char> input, out Version result) =>
diff --git a/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs b/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
index 0c5c05d9e..791f0ed35 100644
--- a/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
+++ b/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
@@ -13,6 +13,7 @@ using Internal.Runtime.Augments;
namespace Internal.DeveloperExperience
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public class DeveloperExperience
{
public virtual void WriteLine(String s)
diff --git a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs
index ebf0d31fb..6eb006f35 100644
--- a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs
+++ b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs
@@ -28,6 +28,7 @@ using EnumInfo = Internal.Runtime.Augments.EnumInfo;
namespace Internal.Reflection.Augments
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public static class ReflectionAugments
{
//
@@ -123,6 +124,7 @@ namespace Internal.Reflection.Augments
// This class is implemented by Internal.Reflection.Core.dll and provides the actual implementation
// of Type.GetTypeInfo() and (on Project N) (Assembly.Load()).
//
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class ReflectionCoreCallbacks
{
public abstract Assembly Load(AssemblyName refName, bool throwOnFileNotFound);
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs
index ff1d19635..7363a9b09 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
namespace Internal.Runtime.Augments
{
+ [ReflectionBlocked]
public sealed class EnumInfo
{
public EnumInfo(Type enumType)
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs
index 71b5063fe..adfe17459 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs
@@ -60,6 +60,9 @@ namespace Internal.Runtime.Augments
// The error message from Win32 is "The filename or extension is too long",
// which is not accurate.
throw new ArgumentException(SR.Argument_LongEnvVarValue);
+ case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY:
+ case Interop.Errors.ERROR_NO_SYSTEM_RESOURCES:
+ throw new OutOfMemoryException(Interop.Kernel32.GetMessage(errorCode));
default:
throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode));
}
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs
index 02c6717a1..eff2b9892 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs
@@ -55,8 +55,6 @@ namespace Internal.Runtime.Augments
private static void ValidateVariableAndValue(string variable, ref string value)
{
- const int MaxEnvVariableValueLength = 32767;
-
if (variable == null)
throw new ArgumentNullException(nameof(variable));
@@ -66,9 +64,6 @@ namespace Internal.Runtime.Augments
if (variable[0] == '\0')
throw new ArgumentException(SR.Argument_StringFirstCharIsZero, nameof(variable));
- if (variable.Length >= MaxEnvVariableValueLength)
- throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable));
-
if (variable.IndexOf('=') != -1)
throw new ArgumentException(SR.Argument_IllegalEnvVarName, nameof(variable));
@@ -77,10 +72,6 @@ namespace Internal.Runtime.Augments
// Explicitly null out value if it's empty
value = null;
}
- else if (value.Length >= MaxEnvVariableValueLength)
- {
- throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(value));
- }
}
public static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables(EnvironmentVariableTarget target)
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs
index 7424ebcc5..fb982fec8 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs
@@ -9,9 +9,12 @@ using System.Runtime.InteropServices;
namespace Internal.Runtime.Augments
{
[CLSCompliant(false)]
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class InteropCallbacks
{
- public abstract bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData delegateData);
+ public abstract IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle);
+
+ public abstract IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate);
public abstract bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub);
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
index cf6b61bfe..c37c0d064 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
@@ -23,6 +23,7 @@ using System.Reflection;
namespace Internal.Runtime.Augments
{
[CLSCompliant(false)]
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class ReflectionExecutionDomainCallbacks
{
// Api's that are exposed in System.Runtime but are really reflection apis.
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
index c8b3b83a1..0213e8821 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
@@ -35,6 +35,7 @@ using Volatile = System.Threading.Volatile;
namespace Internal.Runtime.Augments
{
+ [ReflectionBlocked]
public static class RuntimeAugments
{
/// <summary>
@@ -1074,7 +1075,7 @@ namespace Internal.Runtime.Augments
public static bool FileExists(string path)
{
- return InternalFile.Exists(path);
+ return Internal.IO.File.Exists(path);
}
public static string GetLastResortString(RuntimeTypeHandle typeHandle)
@@ -1082,16 +1083,6 @@ namespace Internal.Runtime.Augments
return typeHandle.LastResortToString;
}
- public static void RhpSetHighLevelDebugFuncEvalHelper(IntPtr highLevelDebugFuncEvalHelper)
- {
- RuntimeImports.RhpSetHighLevelDebugFuncEvalHelper(highLevelDebugFuncEvalHelper);
- }
-
- public static void RhpSetHighLevelDebugFuncEvalAbortHelper(IntPtr highLevelDebugFuncEvalAbortHelper)
- {
- RuntimeImports.RhpSetHighLevelDebugFuncEvalAbortHelper(highLevelDebugFuncEvalAbortHelper);
- }
-
public static void RhpSendCustomEventToDebugger(IntPtr payload, int length)
{
RuntimeImports.RhpSendCustomEventToDebugger(payload, length);
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
index 000b207ce..ca813e6cd 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
@@ -15,6 +15,7 @@ namespace Internal.Runtime.Augments
/// Internal.Runtime.Augments.RuntimeAugments.InitializeStackTraceMetadataSupport(StackTraceMetadataCallbacks callbacks);
///
/// </summary>
+ [System.Runtime.CompilerServices.ReflectionBlocked]
[CLSCompliant(false)]
public abstract class StackTraceMetadataCallbacks
{
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs
index b1b216906..241dae854 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs
@@ -11,6 +11,7 @@ using Internal.Runtime.CompilerServices;
namespace Internal.Runtime.Augments
{
[CLSCompliant(false)]
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class TypeLoaderCallbacks
{
public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle);
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs
index 6d999fb6f..7d3da97a2 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs
@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
//
-// System.Private.CoreLib cannot directly interop with WinRT because the interop DLL depends on System.Private.CoreLib which causes circular dependancy.
+// System.Private.CoreLib cannot directly interop with WinRT because the interop DLL depends on System.Private.CoreLib which causes circular dependency.
// To enable System.Private.CoreLib to call WinRT, we do have another assembly System.Private.WinRTInterop.CoreLib.dll which does the interop with WinRT
// and to allow System.Private.CoreLib to call System.Private.WinRTInterop.CoreLib we do the following trick
// o RmtGen tool will inject code WinRT.Initialize() to the app before the app Main method while building it
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs
index 90a4c5156..ad79c2510 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs
@@ -339,20 +339,15 @@ namespace Internal.Runtime.CompilerHelpers
{
PInvokeMarshal.CoTaskMemFree((IntPtr)p);
}
- /// <summary>
- /// Returns the stub to the pinvoke marshalling stub
- /// </summary>
- public static IntPtr GetStubForPInvokeDelegate(Delegate del)
+
+ public static IntPtr GetFunctionPointerForDelegate(Delegate del)
{
- return PInvokeMarshal.GetStubForPInvokeDelegate(del);
+ return PInvokeMarshal.GetFunctionPointerForDelegate(del);
}
- /// <summary>
- /// Retrieve the corresponding P/invoke instance from the stub
- /// </summary>
- public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType)
+ public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, RuntimeTypeHandle delegateType)
{
- return PInvokeMarshal.GetPInvokeDelegateForStub(pStub, delegateType);
+ return PInvokeMarshal.GetDelegateForFunctionPointer(ptr, delegateType);
}
/// <summary>
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
index 8a1bd9426..81d8a5b4c 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
@@ -9,6 +9,7 @@ using Internal.Runtime.Augments;
namespace Internal.Runtime.CompilerServices
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public static class FunctionPointerOps
{
private struct GenericMethodDescriptorInfo : IEquatable<GenericMethodDescriptorInfo>
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs
index d0daffe26..b9bcd26d4 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs
@@ -6,6 +6,7 @@ using System;
namespace Internal.Runtime.CompilerServices
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public unsafe struct GenericMethodDescriptor
{
internal IntPtr _MethodFunctionPointer;
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs
index 917702235..d207d7ea2 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs
@@ -20,6 +20,7 @@ namespace Internal.Runtime.CompilerServices
// so that repeated allocation of the same resolver will not leak.
// 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of
// a lockless cache so the resolution is very fast for repeated lookups.
+ [ReflectionBlocked]
public struct OpenMethodResolver : IEquatable<OpenMethodResolver>
{
public const short DispatchResolve = 0;
@@ -199,7 +200,7 @@ namespace Internal.Runtime.CompilerServices
public override int GetHashCode()
{
- return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.GetHashCode());
+ return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.IsNull ? 0 : _declaringType.GetHashCode());
}
public bool Equals(OpenMethodResolver other)
diff --git a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs
index a6c2be7be..c1785e5d3 100644
--- a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs
+++ b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs
@@ -24,7 +24,6 @@ internal partial class Interop
CreateSuspended = 0x4u,
WaitAbandoned0 = 0x80u,
WaitTimeout = 0x102u,
- MaxPath = 0x104u,
StackSizeParamIsAReservation = 0x10000u,
Synchronize = 0x100000u,
MaximumAllowed = 0x02000000u,
diff --git a/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData b/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
index 1ac5206dd..840e4bba1 100644
--- a/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
+++ b/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
@@ -72,7 +72,7 @@ internal static extern bool System.Runtime.RuntimeImports.AreTypesAssignable(Sys
internal static extern bool System.Runtime.RuntimeImports.AreTypesEquivalent(System.EETypePtr pType1, System.EETypePtr pType2)
internal static extern double System.Runtime.RuntimeImports.asin(double x)
internal static extern double System.Runtime.RuntimeImports.atan(double x)
-internal static extern double System.Runtime.RuntimeImports.atan2(double x, double y)
+internal static extern double System.Runtime.RuntimeImports.atan2(double y, double x)
internal static extern double System.Runtime.RuntimeImports.ceil(double x)
internal static extern double System.Runtime.RuntimeImports.cos(double x)
internal static extern double System.Runtime.RuntimeImports.cosh(double x)
@@ -219,7 +219,7 @@ private void* System.IntPtr._value
private static volatile System.Collections.Generic.ArraySortHelper<TKey, TValue> System.Collections.Generic.ArraySortHelper<TKey, TValue>.s_defaultArraySortHelper
private static volatile System.Globalization.CultureInfo System.Globalization.CultureInfo.s_DefaultThreadCurrentCulture
private static volatile System.Globalization.CultureInfo System.Globalization.CultureInfo.s_DefaultThreadCurrentUICulture
-private static volatile System.Globalization.CultureInfo System.Globalization.CultureInfo.s_InvariantCultureInfo
+private static readonly System.Globalization.CultureInfo System.Globalization.CultureInfo.s_InvariantCultureInfo
private System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncVoidMethodBuilder.m_task
private System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult> System.Runtime.CompilerServices.AsyncTaskMethodBuilder.m_task
private System.Threading.Tasks.Task<TResult> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>.m_task
diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx
index 24977e34b..2eedd405c 100644
--- a/src/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/System.Private.CoreLib/src/Resources/Strings.resx
@@ -534,6 +534,9 @@
<data name="Argument_DateTimeKindMustBeUnspecified" xml:space="preserve">
<value>The supplied DateTime must have the Kind property set to DateTimeKind.Unspecified.</value>
</data>
+ <data name="Argument_DateTimeKindMustBeUnspecifiedOrUtc" xml:space="preserve">
+ <value>The supplied DateTime must have the Kind property set to DateTimeKind.Unspecified or DateTimeKind.Utc.</value>
+ </data>
<data name="Argument_DateTimeOffsetInvalidDateTimeStyles" xml:space="preserve">
<value>The DateTimeStyles value 'NoCurrentDateDefault' is not allowed when parsing DateTimeOffset.</value>
</data>
@@ -567,15 +570,6 @@
<data name="Argument_FallbackBufferNotEmpty" xml:space="preserve">
<value>Cannot change fallback when buffer is not empty. Previous Convert() call left data in the fallback buffer.</value>
</data>
- <data name="Argument_IdnBadLabelSize" xml:space="preserve">
- <value>IDN labels must be between 1 and 63 characters long.</value>
- </data>
- <data name="Argument_IdnBadPunycode" xml:space="preserve">
- <value>Invalid IDN encoded string.</value>
- </data>
- <data name="Argument_IdnIllegalName" xml:space="preserve">
- <value>Decoded string is not a valid IDN name.</value>
- </data>
<data name="Argument_ImplementIComparable" xml:space="preserve">
<value>At least one object must implement IComparable.</value>
</data>
@@ -1176,6 +1170,9 @@
<data name="InvalidOperation_AsyncFlowCtrlCtxMismatch" xml:space="preserve">
<value>AsyncFlowControl objects can be used to restore flow only on a Context that had its flow suppressed.</value>
</data>
+ <data name="InvalidOperation_AsyncIOInProgress" xml:space="preserve">
+ <value>The stream is currently in use by a previous operation on the stream.</value>
+ </data>
<data name="InvalidProgram_Default" xml:space="preserve">
<value>Common Language Runtime detected an invalid program.</value>
</data>
@@ -1194,6 +1191,18 @@
<data name="InvalidTimeZone_InvalidRegistryData" xml:space="preserve">
<value>The time zone ID '{0}' was found on the local computer, but the registry information was corrupt.</value>
</data>
+ <data name="InvalidTimeZone_InvalidFileData" xml:space="preserve">
+ <value>The time zone ID '{0}' was found on the local computer, but the file at '{1}' was corrupt.</value>
+ </data>
+ <data name="InvalidTimeZone_JulianDayNotSupported" xml:space="preserve">
+ <value>Julian dates in POSIX strings are unsupported.</value>
+ </data>
+ <data name="InvalidTimeZone_NoTTInfoStructures" xml:space="preserve">
+ <value>There are no ttinfo structures in the tzfile. At least one ttinfo structure is required in order to construct a TimeZoneInfo object.</value>
+ </data>
+ <data name="InvalidTimeZone_UnparseablePosixMDateString" xml:space="preserve">
+ <value>'{0}' is not a valid POSIX-TZ-environment-variable MDate rule. A valid rule has the format 'Mm.w.d'.</value>
+ </data>
<data name="IO_DriveNotFound_Drive" xml:space="preserve">
<value>Could not find the drive '{0}'. The drive might not be ready or might not be mapped.</value>
</data>
@@ -1386,7 +1395,7 @@
<data name="Threading_WaitHandleCannotBeOpenedException_InvalidHandle" xml:space="preserve">
<value>A WaitHandle with system-wide name '{0}' cannot be created. A WaitHandle of a different type might have the same name.</value>
</data>
- <data name="TimeZoneNotFound_MissingRegistryData" xml:space="preserve">
+ <data name="TimeZoneNotFound_MissingData" xml:space="preserve">
<value>The time zone ID '{0}' was not found on the local computer.</value>
</data>
<data name="TypeInitialization_Default" xml:space="preserve">
@@ -1855,6 +1864,9 @@
<data name="Format_StringZeroLength" xml:space="preserve">
<value>String cannot have zero length.</value>
</data>
+ <data name="Security_CannotReadFileData" xml:space="preserve">
+ <value>The time zone ID '{0}' was found on the local computer, but the application does not have permission to read the file.</value>
+ </data>
<data name="Security_CannotReadRegistryData" xml:space="preserve">
<value>The time zone ID '{0}' was found on the local computer, but the application does not have permission to read the registry information.</value>
</data>
@@ -1972,9 +1984,6 @@
<data name="IndexOutOfRange_UMSPosition" xml:space="preserve">
<value>Unmanaged memory stream position was beyond the capacity of the stream.</value>
</data>
- <data name="ObjectDisposed_StreamIsClosed" xml:space="preserve">
- <value>Cannot access a closed Stream.</value>
- </data>
<data name="ObjectDisposed_ViewAccessorClosed" xml:space="preserve">
<value>Cannot access a closed accessor.</value>
</data>
@@ -2032,6 +2041,9 @@
<data name="IO_FileStreamHandlePosition" xml:space="preserve">
<value>The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss.</value>
</data>
+ <data name="IO_FileTooLong2GB" xml:space="preserve">
+ <value>The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size.</value>
+ </data>
<data name="NotSupported_FileStreamOnNonFiles" xml:space="preserve">
<value>FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr.</value>
</data>
@@ -2599,6 +2611,9 @@
<data name="Argument_InvalidTypeWithPointersNotSupported" xml:space="preserve">
<value>Cannot use type '{0}'. Only value types without pointers or references are supported.</value>
</data>
+ <data name="Argument_OverlapAlignmentMismatch" xml:space="preserve">
+ <value>Overlapping spans have mismatching alignment.</value>
+ </data>
<data name="ArrayTypeMismatch_ConstrainedCopy" xml:space="preserve">
<value>Array.ConstrainedCopy will only work on array types that are provably compatible, without any form of boxing, unboxing, widening, or casting of each array element. Change the array types (i.e., copy a Derived[] to a Base[]), or use a mitigation strategy in the CER for Array.Copy's less powerful reliability contract, such as cloning the array or throwing away the potentially corrupt destination array.</value>
</data>
@@ -2668,13 +2683,16 @@
<data name="Argument_IdnBadStd3" xml:space="preserve">
<value>Label contains character '{0}' not allowed with UseStd3AsciiRules</value>
</data>
+ <data name="Argument_IdnIllegalName" xml:space="preserve">
+ <value>Decoded string is not a valid IDN name.</value>
+ </data>
<data name="InvalidOperation_NotGenericType" xml:space="preserve">
<value>This operation is only valid on generic types.</value>
</data>
<data name="NotSupported_SignatureType" xml:space="preserve">
<value>This method is not supported on signature types.</value>
</data>
- <data name="Memory_ThrowIfDisposed" xml:space="preserve">
+ <data name="MemoryDisposed" xml:space="preserve">
<value>Memory&lt;T&gt; has been disposed.</value>
</data>
<data name="Memory_OutstandingReferences" xml:space="preserve">
@@ -2686,4 +2704,25 @@
<data name="HashCode_EqualityNotSupported" xml:space="preserve">
<value>HashCode is a mutable struct and should not be compared with other HashCodes.</value>
</data>
+ <data name="ObjectDisposed_WriterClosed" xml:space="preserve">
+ <value>Cannot write to a closed TextWriter.</value>
+ </data>
+ <data name="ObjectDisposed_ReaderClosed" xml:space="preserve">
+ <value>Cannot read from a closed TextReader.</value>
+ </data>
+ <data name="IO_InvalidReadLength" xml:space="preserve">
+ <value>The read operation returned an invalid length.</value>
+ </data>
+ <data name="Arg_BasePathNotFullyQualified" xml:space="preserve">
+ <value>Basepath argument is not fully qualified.</value>
+ </data>
+ <data name="Arg_ElementsInSourceIsGreaterThanDestination" xml:space="preserve">
+ <value>Number of elements in source vector is greater than the destination array</value>
+ </data>
+ <data name="Arg_TypeNotSupported" xml:space="preserve">
+ <value>Specified type is not supported</value>
+ </data>
+ <data name="Arg_NullArgumentNullRef" xml:space="preserve">
+ <value>The method was called with a null array argument.</value>
+ </data>
</root>
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 847dd80f1..9f041c462 100644
--- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -191,7 +191,6 @@
<Compile Include="System\Collections\Generic\CompatibilityEqualityComparers.cs" />
<Compile Include="System\Collections\Generic\EqualityComparer.cs" />
<Compile Include="System\Collections\Generic\EqualOnlyComparer.cs" />
- <Compile Include="System\Collections\LowLevelComparer.cs" />
<Compile Include="System\CurrentSystemTimeZone.Cache.cs" />
<Compile Include="System\Decimal.DecCalc.cs" />
<Compile Include="System\DefaultBinder.CanConvert.cs" />
@@ -203,9 +202,6 @@
<Compile Include="System\Guid.CoreRT.cs" />
<Compile Include="System\InvokeUtils.cs" />
<Compile Include="System\IO\BinaryReader.cs" />
- <Compile Include="System\IO\InternalFile.cs" />
- <Compile Include="System\IO\InternalFile.Windows.cs" Condition="'$(TargetsWindows)'=='true'" />
- <Compile Include="System\IO\InternalFile.Unix.cs" Condition="'$(TargetsUnix)'=='true'" />
<Compile Include="System\IO\FileLoadException.CoreRT.cs" />
<Compile Include="System\IO\Stream.cs" />
<Compile Include="System\RuntimeMethodHandle.cs" />
@@ -214,6 +210,7 @@
<Compile Include="System\Diagnostics\Debug.CoreRT.cs" />
<Compile Include="System\Diagnostics\Debug.Windows.cs" Condition="'$(TargetsWindows)'=='true'" />
<Compile Include="System\Diagnostics\Debugger.cs" />
+ <Compile Include="System\Diagnostics\LowLevelDebugFuncEval.cs" />
<Compile Include="System\Diagnostics\DebuggerGuidedStepThroughAttribute.cs" />
<Compile Include="System\Diagnostics\DebugAnnotations.cs" />
<Compile Include="System\Diagnostics\StackFrame.cs" />
@@ -225,7 +222,6 @@
<Compile Include="System\Enum.cs" />
<Compile Include="System\Environment.cs" />
<Compile Include="System\GC.cs" />
- <Compile Include="System\Globalization\CultureData.cs" />
<Compile Include="System\Globalization\CultureInfo.cs" />
<Compile Include="System\Guid.Windows.cs" Condition="'$(TargetsWindows)'=='true'" />
<Compile Include="System\Guid.Unix.cs" Condition="'$(TargetsUnix)'=='true'" />
@@ -257,7 +253,6 @@
<Compile Include="System\Runtime\CompilerServices\StaticClassConstructionContext.cs" />
<Compile Include="System\Runtime\CompilerServices\NetNativeToolsVersionAttribute.cs" />
<Compile Include="System\Runtime\CompilerServices\FakeElementAttribute.cs" />
- <Compile Include="System\Runtime\CompilerServices\YieldAwaitable.cs" />
<Compile Include="System\Runtime\ConstrainedExecution\CriticalFinalizerObject.cs" />
<Compile Include="System\Runtime\ExceptionIDs.cs" />
<Compile Include="System\Runtime\ExceptionServices\ExceptionDispatchInfo.cs" />
@@ -268,6 +263,7 @@
<Compile Include="System\Runtime\InteropServices\GCHandleType.cs" />
<Compile Include="System\Runtime\InteropServices\InteropExtensions.cs" />
<Compile Include="System\Runtime\InteropServices\NativeCallableAttribute.cs" />
+ <Compile Include="System\Runtime\InteropServices\NativeFunctionPointerWrapper.cs" />
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal.cs" />
<Compile Include="System\Runtime\InteropServices\SafeHandle.cs" />
@@ -277,8 +273,8 @@
<Compile Include="System\Runtime\Serialization\SerializationInfo.cs" />
<Compile Include="System\String.cs" />
<Compile Include="System\String.Comparison.cs" />
+ <Compile Include="System\String.CoreRT.cs" />
<Compile Include="System\String.Intern.cs" />
- <Compile Include="System\String.Manipulation.cs" />
<Compile Include="System\Array.cs" />
<Compile Include="System\Array.CoreRT.cs" />
<Compile Include="System\MDArray.cs" Condition="'$(IsProjectNLibrary)' == 'true'" />
@@ -347,7 +343,6 @@
<Compile Include="System\Threading\WaitHandle.cs" />
<Compile Include="System\Threading\WaitHandleArray.cs" />
<Compile Include="System\ThrowHelper.cs" />
- <Compile Include="System\TimeZoneInfo.cs" />
<Compile Include="System\Type.CoreRT.cs" />
<Compile Include="System\Type.Internal.cs" />
<Compile Condition="'$(TargetsWindows)'=='true' and '$(EnableWinRT)'=='true'" Include="System\Type.WinRt.cs" />
@@ -421,10 +416,8 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)'=='true'">
<Compile Include="Internal\Runtime\Augments\RuntimeThread.Windows.cs" />
- <Compile Include="Microsoft\Win32\SafeHandles\SafeFindHandle.cs" />
<Compile Include="Microsoft\Win32\SafeHandles\SafeThreadPoolIOHandle.cs" />
<Compile Condition="'$(EnableWinRT)' == 'true'" Include="System\TimeZoneInfo.WinRT.cs" />
- <Compile Condition="'$(EnableWinRT)' != 'true'" Include="System\TimeZoneInfo.Win32.cs" />
<Compile Include="System\AppContext.Windows.cs" />
<Compile Include="System\DateTime.Windows.CoreRT.cs" />
<Compile Include="System\Number.Windows.cs" />
@@ -469,9 +462,6 @@
<Compile Condition="'$(EnableWinRT)' != 'true'" Include="..\..\Common\src\Interop\Windows\kernel32\Interop.VirtualAlloc.cs">
<Link>Interop\Windows\kernel32\Interop.VirtualAlloc.cs</Link>
</Compile>
- <Compile Condition="'$(EnableWinRT)' != 'true'" Include="..\..\Common\src\Interop\Windows\mincore\Interop.MUI.cs">
- <Link>Interop\Windows\mincore\Interop.MUI.cs</Link>
- </Compile>
<Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.DateTime.cs">
<Link>Interop\Windows\mincore\Interop.DateTime.cs</Link>
</Compile>
@@ -489,6 +479,7 @@
<Link>Interop\Windows\mincore\Interop.CommandLine.cs</Link>
</Compile>
<Compile Include="System\Environment.Windows.cs" />
+ <Compile Condition="'$(EnableWinRT)' != 'true'" Include="System\Environment.Win32.cs" />
<Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.GetTickCount64.cs">
<Link>Interop\Windows\mincore\Interop.GetTickCount64.cs</Link>
</Compile>
@@ -527,11 +518,28 @@
<Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.ThreadPoolIO.cs">
<Link>Interop\Windows\mincore\Interop.ThreadPoolIO.cs</Link>
</Compile>
+ <Compile Condition="'$(EnableWinRT)' == 'true'" Include="..\..\Common\src\Interop\Windows\mincore\Interop.TimeZone.cs">
+ <Link>Interop\Windows\mincore\Interop.TimeZone.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\Interop.Libraries.cs">
+ <Link>Interop\Windows\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.GetLastError.cs">
+ <Link>Interop\Windows\mincore\Interop.GetLastError.cs</Link>
+ </Compile>
+ <Compile Condition="'$(EnableWinRT)' != 'true'" Include="..\..\Common\src\Interop\Windows\mincore\Interop.GetSystemDirectory.cs">
+ <Link>Interop\Windows\mincore\Interop.GetSystemDirectory.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.CoCreateGuid.cs">
+ <Link>Interop\Windows\mincore\Interop.CoCreateGuid.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.QueryUnbiasedInterruptTime.cs">
+ <Link>Interop\Windows\mincore\Interop.QueryUnbiasedInterruptTime.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)'=='true' And '$(EnableDummyGlobalizationImplementation)' != 'true'">
<Compile Include="System\Globalization\CultureInfo.Windows.cs" />
<Compile Include="System\Globalization\CompareInfo.Windows.cs" />
- <Compile Include="System\Globalization\CultureData.Windows.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeaturePortableThreadPool)' == 'true'">
<Compile Include="System\Threading\ThreadPool.Portable.cs" />
@@ -546,30 +554,6 @@
<Compile Include="System\Threading\ClrThreadPool.CpuUtilizationReader.Unix.cs" Condition="'$(TargetsUnix)' == 'true'" />
<Compile Include="System\Threading\ClrThreadPool.CpuUtilizationReader.Windows.cs" Condition="'$(TargetsWindows)'=='true'" />
</ItemGroup>
- <ItemGroup>
- <!-- CORERT-TODO: Port to Unix -->
- <Compile Include="..\..\Common\src\Interop\Windows\Interop.Libraries.cs">
- <Link>Interop\Windows\Interop.Libraries.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.GetLastError.cs">
- <Link>Interop\Windows\mincore\Interop.GetLastError.cs</Link>
- </Compile>
- <Compile Condition="'$(EnableWinRT)' != 'true'" Include="..\..\Common\src\Interop\Windows\mincore\Interop.GetSystemDirectory.cs">
- <Link>Interop\Windows\mincore\Interop.GetSystemDirectory.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.TimeZone.cs">
- <Link>Interop\Windows\mincore\Interop.TimeZone.cs</Link>
- </Compile>
- <Compile Condition="'$(EnableWinRT)' != 'true'" Include="..\..\Common\src\Interop\Windows\mincore\Interop.TimeZone.Win32.cs">
- <Link>Interop\Windows\mincore\Interop.TimeZone.Win32.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.CoCreateGuid.cs">
- <Link>Interop\Windows\mincore\Interop.CoCreateGuid.cs</Link>
- </Compile>
- <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.QueryUnbiasedInterruptTime.cs">
- <Link>Interop\Windows\mincore\Interop.QueryUnbiasedInterruptTime.cs</Link>
- </Compile>
- </ItemGroup>
<!-- Dummy globalization implementation -->
<ItemGroup Condition="'$(EnableDummyGlobalizationImplementation)' == 'true'">
<Compile Include="System\Globalization\HijriCalendar.Dummy.cs" />
@@ -589,7 +573,6 @@
<ItemGroup Condition="'$(TargetsUnix)'=='true'">
<Compile Include="Internal\Runtime\Augments\RuntimeThread.Unix.cs" />
<Compile Include="System\AppContext.Unix.cs" />
- <Compile Include="System\TimeZoneInfo.Unix.cs" />
<Compile Include="System\DateTime.Unix.CoreRT.cs" />
<Compile Include="System\Environment.Unix.cs" />
<Compile Include="System\Number.Unix.cs" />
@@ -695,9 +678,6 @@
<Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs">
<Link>System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs</Link>
</Compile>
- <Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgPInvokeData.cs">
- <Link>System\Runtime\InteropServices\McgPInvokeData.cs</Link>
- </Compile>
<Compile Include="..\..\Common\src\System\NotImplemented.cs">
<Link>System\NotImplemented.cs</Link>
</Compile>
@@ -784,7 +764,7 @@
</ItemGroup>
<ItemGroup Condition="'$(InPlaceRuntime)' == 'true'">
<Compile Condition="'$(Platform)'!='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Full\AsmOffsets.cs" />
- <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsets.cs" />
+ <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsetsPortable.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml">
diff --git a/src/System.Private.CoreLib/src/System/AppContext.Windows.cs b/src/System.Private.CoreLib/src/System/AppContext.Windows.cs
index 7d535c5de..a53e84813 100644
--- a/src/System.Private.CoreLib/src/System/AppContext.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/AppContext.Windows.cs
@@ -19,7 +19,7 @@ namespace System
/// </summary>
private static string GetBaseDirectoryCore()
{
- StringBuilder buffer = new StringBuilder(Interop.mincore.MAX_PATH);
+ StringBuilder buffer = new StringBuilder(Interop.Kernel32.MAX_PATH);
while (true)
{
int size = Interop.mincore.GetModuleFileName(IntPtr.Zero, buffer, buffer.Capacity);
diff --git a/src/System.Private.CoreLib/src/System/Array.CoreRT.cs b/src/System.Private.CoreLib/src/System/Array.CoreRT.cs
index 9ae235f14..80c4d2bbf 100644
--- a/src/System.Private.CoreLib/src/System/Array.CoreRT.cs
+++ b/src/System.Private.CoreLib/src/System/Array.CoreRT.cs
@@ -942,7 +942,7 @@ namespace System
{
public ComparerAsComparerT(IComparer comparer)
{
- _comparer = (comparer == null) ? LowLevelComparer.Default : comparer;
+ _comparer = (comparer == null) ? Comparer.Default : comparer;
}
public int Compare(Object x, Object y)
diff --git a/src/System.Private.CoreLib/src/System/Array.cs b/src/System.Private.CoreLib/src/System/Array.cs
index 184964df1..2822401ff 100644
--- a/src/System.Private.CoreLib/src/System/Array.cs
+++ b/src/System.Private.CoreLib/src/System/Array.cs
@@ -466,7 +466,7 @@ namespace System
if (array.Rank != 1)
throw new RankException(SR.Rank_MultiDimNotSupported);
- if (comparer == null) comparer = LowLevelComparer.Default;
+ if (comparer == null) comparer = Comparer.Default;
int lo = index;
int hi = index + length - 1;
diff --git a/src/System.Private.CoreLib/src/System/Buffer.cs b/src/System.Private.CoreLib/src/System/Buffer.cs
index 5e31e69fd..b8c840e42 100644
--- a/src/System.Private.CoreLib/src/System/Buffer.cs
+++ b/src/System.Private.CoreLib/src/System/Buffer.cs
@@ -15,8 +15,10 @@ using System.Runtime.CompilerServices;
using Internal.Runtime.CompilerServices;
#if BIT64
+using nint = System.Int64;
using nuint = System.UInt64;
#else
+using nint = System.Int32;
using nuint = System.UInt32;
#endif
@@ -434,6 +436,248 @@ namespace System
_Memmove(dest, src, len);
}
+ // This method has different signature for x64 and other platforms and is done for performance reasons.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void Memmove<T>(ref T destination, ref T source, nuint elementCount)
+ {
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ // Blittable memmove
+#if PROJECTN
+ unsafe
+ {
+ fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination), pSource = &Unsafe.As<T, byte>(ref source))
+ Memmove(pDestination, pSource, elementCount * (nuint)Unsafe.SizeOf<T>());
+ }
+#else
+ Memmove(
+ new ByReference<byte>(ref Unsafe.As<T, byte>(ref destination)),
+ new ByReference<byte>(ref Unsafe.As<T, byte>(ref source)),
+ elementCount * (nuint)Unsafe.SizeOf<T>());
+#endif
+ }
+ else
+ {
+ // Non-blittable memmove
+
+ // Try to avoid calling RhBulkMoveWithWriteBarrier if we can get away
+ // with a no-op.
+ if (!Unsafe.AreSame(ref destination, ref source) && elementCount != 0)
+ {
+ RuntimeImports.RhBulkMoveWithWriteBarrier(
+ ref Unsafe.As<T, byte>(ref destination),
+ ref Unsafe.As<T, byte>(ref source),
+ elementCount * (nuint)Unsafe.SizeOf<T>());
+ }
+ }
+ }
+
+#if !PROJECTN
+ // This method has different signature for x64 and other platforms and is done for performance reasons.
+ private static void Memmove(ByReference<byte> dest, ByReference<byte> src, nuint len)
+ {
+#if AMD64 || (BIT32 && !ARM)
+ const nuint CopyThreshold = 2048;
+#elif ARM64
+#if PLATFORM_WINDOWS
+ // Determined optimal value for Windows.
+ // https://github.com/dotnet/coreclr/issues/13843
+ const nuint CopyThreshold = UInt64.MaxValue;
+#else // PLATFORM_WINDOWS
+ // Managed code is currently faster than glibc unoptimized memmove
+ // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
+ // https://github.com/dotnet/coreclr/issues/13844
+ const nuint CopyThreshold = UInt64.MaxValue;
+#endif // PLATFORM_WINDOWS
+#else
+ const nuint CopyThreshold = 512;
+#endif // AMD64 || (BIT32 && !ARM)
+
+ // P/Invoke into the native version when the buffers are overlapping.
+
+ if (((nuint)Unsafe.ByteOffset(ref src.Value, ref dest.Value) < len) || ((nuint)Unsafe.ByteOffset(ref dest.Value, ref src.Value) < len))
+ {
+ goto BuffersOverlap;
+ }
+
+ // Use "(IntPtr)(nint)len" to avoid overflow checking on the explicit cast to IntPtr
+
+ ref byte srcEnd = ref Unsafe.Add(ref src.Value, (IntPtr)(nint)len);
+ ref byte destEnd = ref Unsafe.Add(ref dest.Value, (IntPtr)(nint)len);
+
+ if (len <= 16)
+ goto MCPY02;
+ if (len > 64)
+ goto MCPY05;
+
+MCPY00:
+// Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
+ Debug.Assert(len > 16 && len <= 64);
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block16>(ref dest.Value) = Unsafe.As<byte, Block16>(ref src.Value); // [0,16]
+#elif BIT64
+ Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 8)); // [0,16]
+#else
+ Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 8));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 12)); // [0,16]
+#endif
+ if (len <= 32)
+ goto MCPY01;
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src.Value, 16)); // [0,32]
+#elif BIT64
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 16));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 24)); // [0,32]
+#else
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 16));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 20));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 24));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 28)); // [0,32]
+#endif
+ if (len <= 48)
+ goto MCPY01;
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src.Value, 32)); // [0,48]
+#elif BIT64
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 32));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 40)); // [0,48]
+#else
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 32));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 36));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 40));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 44)); // [0,48]
+#endif
+
+MCPY01:
+// Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
+ Debug.Assert(len > 16 && len <= 64);
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
+#elif BIT64
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
+#else
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
+#endif
+ return;
+
+MCPY02:
+// Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
+ if ((len & 24) == 0)
+ goto MCPY03;
+ Debug.Assert(len >= 8 && len <= 16);
+#if BIT64
+ Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
+#else
+ Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
+#endif
+ return;
+
+MCPY03:
+// Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
+ if ((len & 4) == 0)
+ goto MCPY04;
+ Debug.Assert(len >= 4 && len < 8);
+ Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
+ return;
+
+MCPY04:
+// Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
+ Debug.Assert(len < 4);
+ if (len == 0)
+ return;
+ dest.Value = src.Value;
+ if ((len & 2) == 0)
+ return;
+ Unsafe.As<byte, short>(ref Unsafe.Add(ref destEnd, -2)) = Unsafe.As<byte, short>(ref Unsafe.Add(ref srcEnd, -2));
+ return;
+
+MCPY05:
+// PInvoke to the native version when the copy length exceeds the threshold.
+ if (len > CopyThreshold)
+ {
+ goto PInvoke;
+ }
+ // Copy 64-bytes at a time until the remainder is less than 64.
+ // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
+ Debug.Assert(len > 64 && len <= CopyThreshold);
+ nuint n = len >> 6;
+
+MCPY06:
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block64>(ref dest.Value) = Unsafe.As<byte, Block64>(ref src.Value);
+#elif BIT64
+ Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 8));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 16));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 24));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 32));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 40));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 48)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 48));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 56)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 56));
+#else
+ Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 8));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 12));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 16));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 20));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 24));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 28));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 32));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 36));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 40));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 44));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 48)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 48));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 52)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 52));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 56)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 56));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 60)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 60));
+#endif
+ dest = new ByReference<byte>(ref Unsafe.Add(ref dest.Value, 64));
+ src = new ByReference<byte>(ref Unsafe.Add(ref src.Value, 64));
+ n--;
+ if (n != 0)
+ goto MCPY06;
+
+ len %= 64;
+ if (len > 16)
+ goto MCPY00;
+#if HAS_CUSTOM_BLOCKS
+ Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
+#elif BIT64
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
+ Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
+#else
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
+ Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
+#endif
+ return;
+
+BuffersOverlap:
+ // If the buffers overlap perfectly, there's no point to copying the data.
+ if (Unsafe.AreSame(ref dest.Value, ref src.Value))
+ {
+ return;
+ }
+
+PInvoke:
+ _Memmove(ref dest.Value, ref src.Value, len);
+ }
+#endif // !PROJECTN
+
// Non-inlinable wrapper around the QCall that avoids poluting the fast path
// with P/Invoke prolog/epilog.
[MethodImplAttribute(MethodImplOptions.NoInlining)]
@@ -442,6 +686,16 @@ namespace System
RuntimeImports.memmove(dest, src, len);
}
+ // Non-inlinable wrapper around the QCall that avoids polluting the fast path
+ // with P/Invoke prolog/epilog.
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ private unsafe static void _Memmove(ref byte dest, ref byte src, nuint len)
+ {
+ fixed (byte* pDest = &dest)
+ fixed (byte* pSrc = &src)
+ RuntimeImports.memmove(pDest, pSrc, len);
+ }
+
#if HAS_CUSTOM_BLOCKS
[StructLayout(LayoutKind.Explicit, Size = 16)]
private struct Block16 { }
diff --git a/src/System.Private.CoreLib/src/System/Collections/LowLevelComparer.cs b/src/System.Private.CoreLib/src/System/Collections/LowLevelComparer.cs
deleted file mode 100644
index c6da9b58f..000000000
--- a/src/System.Private.CoreLib/src/System/Collections/LowLevelComparer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//
-// LowLevelComparer emulates the desktop type System.Collections.Comparer.
-//
-
-using System;
-using System.Globalization;
-using System.Diagnostics;
-
-namespace System.Collections
-{
- internal sealed class LowLevelComparer : IComparer
- {
- internal static readonly LowLevelComparer Default = new LowLevelComparer();
-
- private LowLevelComparer()
- {
- }
-
- public int Compare(Object a, Object b)
- {
- if (a == b) return 0;
- if (a == null) return -1;
- if (b == null) return 1;
-
- IComparable ia = a as IComparable;
- if (ia != null)
- return ia.CompareTo(b);
-
- IComparable ib = b as IComparable;
- if (ib != null)
- return -ib.CompareTo(a);
-
- throw new ArgumentException(SR.Argument_ImplementIComparable);
- }
- }
-}
diff --git a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
index 5e67f3f61..05afe6b7e 100644
--- a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
+++ b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System
@@ -30,7 +31,7 @@ namespace System
internal bool IsNegative
{
- get { return (uflags & SignMask) != 0; }
+ get { return flags < 0; }
set { uflags = (uflags & ~SignMask) | (value ? SignMask : 0); }
}
@@ -85,11 +86,8 @@ namespace System
private const int DEC_SCALE_MAX = 28;
- private const uint SNGBIAS = 126;
- private const uint DBLBIAS = 1022;
-
private const uint TenToPowerNine = 1000000000;
- private static readonly Split64 s_tenToPowerEighteen = new Split64() { int64 = 1000000000000000000 };
+ private const ulong TenToPowerEighteen = 1000000000000000000;
// The maximum power of 10 that a 32 bit integer can store
private const Int32 MaxInt32Scale = 9;
@@ -145,9 +143,6 @@ namespace System
1e80
};
- // Value taken via reverse engineering the double that corrisponds to 2^65. (oleaut32 has ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0))
- private const double ds2to64 = 1.8446744073709552e+019;
-
#region Decimal Math Helpers
private static unsafe uint GetExponent(float f)
@@ -159,8 +154,7 @@ namespace System
// ULONG sign:1;
//} SNGSTRUCT;
- uint* pf = (uint*)&f;
- return (*pf >> 23) & 0xFFu;
+ return (byte)(*(uint*)&f >> 23);
}
private static unsafe uint GetExponent(double d)
@@ -171,34 +165,7 @@ namespace System
// DWORDLONG signexp:12;
// } DBLSTRUCT;
- ulong* pd = (ulong*)&d;
- return (uint)(*pd >> 52) & 0x7FFu;
- }
-
- // Use table but enable for computation if necessary
- private static double GetDoublePower10(int ix)
- {
- if (ix >= 0 && ix < s_doublePowers10.Length)
- return s_doublePowers10[ix];
- return Math.Pow(10, ix);
- }
-
- private static ulong DivMod64by32(ulong num, uint den)
- {
- Split64 sdl = new Split64();
-
- sdl.Low32 = (uint)(num / den);
- sdl.High32 = (uint)(num % den);
- return sdl.int64;
- }
-
- private static ulong DivMod32by32(uint num, uint den)
- {
- Split64 sdl = new Split64();
-
- sdl.Low32 = num / den;
- sdl.High32 = num % den;
- return sdl.int64;
+ return (uint)(*(ulong*)&d >> 52) & 0x7FFu;
}
private static ulong UInt32x32To64(uint a, uint b)
@@ -206,26 +173,24 @@ namespace System
return (ulong)a * (ulong)b;
}
- private static ulong UInt64x64To128(Split64 sdlOp1, Split64 sdlOp2, out ulong dlHi)
+ private static ulong UInt64x64To128(ulong a, ulong b, out ulong dlHi)
{
- Split64 sdlTmp1 = new Split64();
- Split64 sdlTmp2 = new Split64();
- Split64 sdlTmp3 = new Split64();
-
- sdlTmp1.int64 = UInt32x32To64(sdlOp1.Low32, sdlOp2.Low32); // lo partial prod
- sdlTmp2.int64 = UInt32x32To64(sdlOp1.Low32, sdlOp2.High32); // mid 1 partial prod
- sdlTmp1.High32 += sdlTmp2.Low32;
- if (sdlTmp1.High32 < sdlTmp2.Low32) // test for carry
- sdlTmp2.High32++;
- sdlTmp3.int64 = UInt32x32To64(sdlOp1.High32, sdlOp2.High32) + sdlTmp2.High32;
- sdlTmp2.int64 = UInt32x32To64(sdlOp1.High32, sdlOp2.Low32);
- sdlTmp1.High32 += sdlTmp2.Low32;
- if (sdlTmp1.High32 < sdlTmp2.Low32) // test for carry
- sdlTmp2.High32++;
- sdlTmp3.int64 += sdlTmp2.High32;
-
- dlHi = sdlTmp3.int64;
- return sdlTmp1.int64;
+ ulong low = (uint)a * (ulong)(uint)b; // lo partial prod
+ ulong mid = (uint)a * (b >> 32); // mid 1 partial prod
+ ulong high = (a >> 32) * (b >> 32);
+ high += mid >> 32;
+ low += mid <<= 32;
+ if (low < mid) // test for carry
+ high++;
+
+ mid = (a >> 32) * (uint)b;
+ high += mid >> 32;
+ low += mid <<= 32;
+ if (low < mid) // test for carry
+ high++;
+
+ dlHi = high;
+ return low;
}
/***
@@ -256,12 +221,16 @@ namespace System
div = tmp / ulDen;
bufNum.High64 = div;
tmp = ((tmp - div * ulDen) << 32) | bufNum.U0;
+ if (tmp == 0)
+ return 0;
uint div32 = (uint)(tmp / ulDen);
bufNum.U0 = div32;
return (uint)tmp - div32 * ulDen;
}
tmp = bufNum.Low64;
+ if (tmp == 0)
+ return 0;
div = tmp / ulDen;
bufNum.Low64 = div;
return (uint)(tmp - div * ulDen);
@@ -571,31 +540,7 @@ PosRem:
if (iHiRes > 2)
{
iNewScale = (int)iHiRes * 32 - 64 - 1;
- // Find the MSB.
- //
- uint ulTmp = rgulRes[iHiRes];
- iNewScale--;
- if ((ulTmp & 0xFFFF0000) == 0)
- {
- ulTmp <<= 16;
- iNewScale -= 16;
- }
- if ((ulTmp & 0xFF000000) == 0)
- {
- ulTmp <<= 8;
- iNewScale -= 8;
- }
- if ((ulTmp & 0xF0000000) == 0)
- {
- ulTmp <<= 4;
- iNewScale -= 4;
- }
- if ((ulTmp & 0xC0000000) == 0)
- {
- ulTmp <<= 2;
- iNewScale -= 2;
- }
- iNewScale -= (int)ulTmp >> 31;
+ iNewScale -= LeadingZeroCount(rgulRes[iHiRes]);
// Multiply bit position by log10(2) to figure it's power of 10.
// We scale the log by 256. log(2) = .30103, * 256 = 77. Doing this
@@ -811,6 +756,38 @@ ThrowOverflow:
throw new OverflowException(SR.Overflow_Decimal);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LeadingZeroCount(uint value)
+ {
+#if CORECLR
+ if (System.Runtime.Intrinsics.X86.Lzcnt.IsSupported)
+ return (int)System.Runtime.Intrinsics.X86.Lzcnt.LeadingZeroCount(value);
+#endif
+
+ int c = 1;
+ if ((value & 0xFFFF0000) == 0)
+ {
+ value <<= 16;
+ c += 16;
+ }
+ if ((value & 0xFF000000) == 0)
+ {
+ value <<= 8;
+ c += 8;
+ }
+ if ((value & 0xF0000000) == 0)
+ {
+ value <<= 4;
+ c += 4;
+ }
+ if ((value & 0xC0000000) == 0)
+ {
+ value <<= 2;
+ c += 2;
+ }
+ return c + ((int)value >> 31);
+ }
+
// Adjust the quotient to deal with an overflow. We need to divide by 10,
// feed in the high bit to undo the overflow and then round as required,
private static int OverflowUnscale(ref Buf12 bufQuo, int iScale, bool fRemainder)
@@ -818,21 +795,20 @@ ThrowOverflow:
if (--iScale < 0)
throw new OverflowException(SR.Overflow_Decimal);
- Split64 sdlTmp = new Split64();
+ Debug.Assert(bufQuo.U2 == 0);
// We have overflown, so load the high bit with a one.
- sdlTmp.High32 = 1;
- sdlTmp.Low32 = bufQuo.U2;
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U2 = sdlTmp.Low32;
- sdlTmp.Low32 = bufQuo.U1;
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U1 = sdlTmp.Low32;
- sdlTmp.Low32 = bufQuo.U0;
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U0 = sdlTmp.Low32;
+ const ulong highbit = 1UL << 32;
+ bufQuo.U2 = (uint)(highbit / 10);
+ ulong tmp = ((highbit % 10) << 32) + bufQuo.U1;
+ uint div = (uint)(tmp / 10);
+ bufQuo.U1 = div;
+ tmp = ((tmp - div * 10) << 32) + bufQuo.U0;
+ div = (uint)(tmp / 10);
+ bufQuo.U0 = div;
+ uint remainder = (uint)(tmp - div * 10);
// The remainder is the last digit that does not fit, so we can use it to work out if we need to round up
- if ((sdlTmp.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (bufQuo.U0 & 1) != 0)))
+ if (remainder > 5 || remainder == 5 && (fRemainder || (bufQuo.U0 & 1) != 0))
Add32To96(ref bufQuo, 1);
return iScale;
}
@@ -1273,208 +1249,51 @@ ReturnResult:
return;
}
- // Returns the absolute value of the given Decimal. If d is
- // positive, the result is d. If d is negative, the result
- // is -d.
- //
- private static Decimal Abs(Decimal d)
- {
- return new Decimal(d.lo, d.mid, d.hi, (int)(d.uflags & ~SignMask));
- }
-
- /***
-* DecFixInt
-*
-* input - Pointer to Decimal operand
-* result - Pointer to Decimal result location
-*
-* Purpose:
-* Chop the value to integer. Return remainder so Int() function
-* can round down if non-zero.
-*
-* Exit:
-* Returns remainder.
-*
-* Exceptions:
-* None.
-*
-***********************************************************************/
- private static unsafe uint DecFixInt(ref Decimal input, ref Decimal result)
- {
- Buf12 bufNum;
- _ = &bufNum; // workaround for CS0165
- uint remainder;
- uint power;
- int scale;
-
- if (input.Scale > 0)
- {
- bufNum.U0 = input.ulo;
- bufNum.U1 = input.umid;
- bufNum.U2 = input.uhi;
- scale = input.Scale;
- result.IsNegative = input.IsNegative;
- remainder = 0;
-
- do
- {
- if (scale > MaxInt32Scale)
- power = TenToPowerNine;
- else
- power = s_powers10[scale];
-
- remainder |= Div96By32(ref bufNum, power);
- scale -= MaxInt32Scale;
- } while (scale > 0);
-
- result.ulo = bufNum.U0;
- result.umid = bufNum.U1;
- result.uhi = bufNum.U2;
- result.Scale = 0;
-
- return remainder;
- }
- result = input;
- return 0;
- }
-
- #endregion
+#endregion
//**********************************************************************
// VarCyFromDec - Convert Currency to Decimal (similar to OleAut32 api.)
//**********************************************************************
- internal static void VarCyFromDec(ref Decimal pdecIn, out long pcyOut)
+ internal static long VarCyFromDec(ref Decimal pdecIn)
{
- if (!Decimal.IsValid(pdecIn.uflags))
- throw new OverflowException(SR.Overflow_Currency);
-
- Split64 sdlTmp = default(Split64);
-
- int scale = pdecIn.Scale - 4; // the power of 10 to divide by
- if (scale == 0)
- {
- if (pdecIn.High != 0 ||
- (pdecIn.Mid >= 0x80000000U &&
- (pdecIn.Mid != 0x80000000U || pdecIn.Low != 0 || !pdecIn.IsNegative)))
- throw new OverflowException(SR.Overflow_Currency);
-
- sdlTmp.Low32 = pdecIn.Low;
- sdlTmp.High32 = pdecIn.Mid;
-
- if (pdecIn.IsNegative)
- pcyOut = -(long)sdlTmp.int64;
- else
- pcyOut = (long)sdlTmp.int64;
- return;
- }
+ long value;
+ int scale = pdecIn.Scale - 4;
// Need to scale to get 4 decimal places. -4 <= scale <= 24.
//
if (scale < 0)
{
- Split64 sdlTmp1 = default(Split64);
- sdlTmp1.int64 = UInt32x32To64(s_powers10[-scale], pdecIn.Mid);
- sdlTmp.int64 = UInt32x32To64(s_powers10[-scale], pdecIn.Low);
- sdlTmp.High32 += sdlTmp1.Low32;
- if (pdecIn.High != 0 || sdlTmp1.High32 != 0 || sdlTmp1.Low32 > sdlTmp.High32)
- throw new OverflowException(SR.Overflow_Currency);
- }
- else if (scale < 10)
- {
- // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
- //
- uint pwr = s_powers10[scale];
- if (pdecIn.High >= pwr)
- throw new OverflowException(SR.Overflow_Currency);
-
- Split64 sdlTmp1 = default(Split64);
- sdlTmp1.Low32 = pdecIn.Mid;
- sdlTmp1.High32 = pdecIn.High;
- sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, pwr);
- sdlTmp.High32 = sdlTmp1.Low32; // quotient to high half of result
- sdlTmp1.Low32 = pdecIn.Low; // extended remainder
- sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, pwr);
- sdlTmp.Low32 = sdlTmp1.Low32; // quotient to low half of result
-
- // Round result based on remainder in sdlTmp1.Hi.
- //
- pwr >>= 1; // compare to power/2 (power always even)
- if (sdlTmp1.High32 > pwr || (sdlTmp1.High32 == pwr && ((sdlTmp.Low32 & 1) != 0)))
- sdlTmp.int64++;
+ if (pdecIn.High != 0)
+ goto ThrowOverflow;
+ uint pwr = s_powers10[-scale];
+ ulong high = UInt32x32To64(pwr, pdecIn.Mid);
+ if (high > uint.MaxValue)
+ goto ThrowOverflow;
+ ulong low = UInt32x32To64(pwr, pdecIn.Low);
+ low += high <<= 32;
+ if (low < high)
+ goto ThrowOverflow;
+ value = (long)low;
}
else
{
- // We have a power of 10 in the range 10 - 24. These powers do
- // not fit in 32 bits. We'll handle this by scaling 2 or 3 times,
- // first by 10^10, then by the remaining amount (or 10^9, then
- // the last bit).
- //
- // To scale by 10^10, we'll actually divide by 10^10/4, which fits
- // in 32 bits. The second scaling is multiplied by four
- // to account for it, just barely assured of fitting in 32 bits
- // (4E9 < 2^32). Note that the upper third of the quotient is
- // either zero or one, so we skip the divide step to calculate it.
- // (Max 4E9 divided by 2.5E9.)
- //
- // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
- //
-
- const uint TenToTenDiv4 = 2500000000U;
-
- Split64 sdlTmp1 = default(Split64);
- if (pdecIn.High >= TenToTenDiv4)
- {
- sdlTmp.High32 = 1; // upper 1st quotient
- sdlTmp1.High32 = pdecIn.High - TenToTenDiv4; // remainder
- }
- else
- {
- sdlTmp.High32 = 0; // upper 1st quotient
- sdlTmp1.High32 = pdecIn.High; // remainder
- }
-
- sdlTmp1.Low32 = pdecIn.Mid; // extended remainder
- sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, TenToTenDiv4);
- sdlTmp.Low32 = sdlTmp1.Low32; // middle 1st quotient
-
- sdlTmp1.Low32 = pdecIn.Low; // extended remainder
- sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, TenToTenDiv4);
-
- uint pwr = s_powers10[Math.Min(scale - 10, 9)] << 2;
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, pwr);
- uint savedTmpLow32 = sdlTmp.Low32; // upper 2nd quotient
-
- sdlTmp.Low32 = sdlTmp1.Low32; // extended remainder
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, pwr);
- sdlTmp1.Low32 = sdlTmp.High32; // save final remainder
- sdlTmp.High32 = savedTmpLow32; // position high result
-
- if (scale >= 20)
- {
- pwr = s_powers10[scale - 19];
- sdlTmp.int64 = DivMod64by32(sdlTmp.int64, pwr);
- sdlTmp1.High32 |= sdlTmp1.Low32; // combine sticky bits
- sdlTmp1.Low32 = sdlTmp.High32; // final remainder
- sdlTmp.High32 = 0; // guaranteed result fits in 32 bits
- }
-
- // Round result based on remainder in sdlTmp1.Lo. sdlTmp1.Hi is
- // the remainder from the first division(s), representing sticky bits.
- // Current result is in sdlTmp.
-
- pwr >>= 1; // compare to power/2 (power always even)
- if (sdlTmp1.Low32 > pwr || (sdlTmp1.Low32 == pwr && (((sdlTmp.Low32 & 1) != 0) || sdlTmp1.High32 != 0)))
- sdlTmp.int64++;
+ if (scale != 0)
+ InternalRound(ref pdecIn, (uint)scale, RoundingMode.ToEven);
+ if (pdecIn.High != 0)
+ goto ThrowOverflow;
+ value = (long)pdecIn.Low64;
}
- if (sdlTmp.High32 >= 0x80000000U &&
- (sdlTmp.int64 != 0x8000000000000000LU || !pdecIn.IsNegative))
- throw new OverflowException(SR.Overflow_Currency);
+ if (value < 0 && (value != long.MinValue || !pdecIn.IsNegative))
+ goto ThrowOverflow;
if (pdecIn.IsNegative)
- sdlTmp.int64 = (ulong)(-(long)sdlTmp.int64);
+ value = -value;
+
+ return value;
- pcyOut = (long)sdlTmp.int64;
+ThrowOverflow:
+ throw new OverflowException(SR.Overflow_Currency);
}
//**********************************************************************
@@ -1506,6 +1325,9 @@ ReturnResult:
ulong low64 = d1.Low64;
uint high = d1.High;
+ ulong d2Low64 = d2.Low64;
+ uint d2High = d2.High;
+
if (iScale != 0)
{
iScale >>= ScaleShift;
@@ -1517,9 +1339,14 @@ ReturnResult:
// Guessed scale factor wrong. Swap operands.
iScale = -iScale;
sign = -sign;
- low64 = d2.Low64;
- high = d2.High;
- d2 = d1;
+
+ ulong tmp64 = low64;
+ low64 = d2Low64;
+ d2Low64 = tmp64;
+
+ uint tmp = high;
+ high = d2High;
+ d2High = tmp;
}
// d1 will need to be multiplied by 10^iScale so it will have the same scale as d2.
@@ -1539,7 +1366,7 @@ ReturnResult:
} while ((iScale -= MaxInt32Scale) > 0);
}
- uint cmpHigh = high - d2.High;
+ uint cmpHigh = high - d2High;
if (cmpHigh != 0)
{
// check for overflow
@@ -1548,7 +1375,7 @@ ReturnResult:
return sign;
}
- ulong cmpLow64 = low64 - d2.Low64;
+ ulong cmpLow64 = low64 - d2Low64;
if (cmpLow64 == 0)
sign = 0;
// check for overflow
@@ -1712,28 +1539,31 @@ ReturnZero:
//**********************************************************************
// VarDecFromR4 - Convert float to Decimal
//**********************************************************************
+#if PROJECTN // Workaround ProjectN bug #555233
+ [MethodImplAttribute(MethodImplOptions.NoOptimization)]
+#endif
internal static void VarDecFromR4(float input, out Decimal pdecOut)
{
- int iExp; // number of bits to left of binary point
- int iPower;
- uint ulMant;
- double dbl;
- Split64 sdlLo = new Split64();
- Split64 sdlHi = new Split64();
- int lmax, cur; // temps used during scale reduction
-
pdecOut = new Decimal();
// The most we can scale by is 10^28, which is just slightly more
// than 2^93. So a float with an exponent of -94 could just
// barely reach 0.5, but smaller exponents will always round to zero.
//
- iExp = (int)(GetExponent(input) - SNGBIAS);
+ const uint SNGBIAS = 126;
+ int iExp = (int)(GetExponent(input) - SNGBIAS);
if (iExp < -94)
return; // result should be zeroed out
if (iExp > 96)
- throw new OverflowException(SR.Overflow_Decimal);
+ goto ThrowOverflow;
+
+ uint flags = 0;
+ if (input < 0)
+ {
+ input = -input;
+ flags = SignMask;
+ }
// Round the input to a 7-digit integer. The R4 format has
// only 7 digits of precision, and we want to keep garbage digits
@@ -1743,10 +1573,9 @@ ReturnZero:
// the exponent by log10(2). Using scaled integer multiplcation,
// log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
//
- dbl = input;
- if (dbl < 0)
- dbl *= -1;
- iPower = 6 - ((iExp * 19728) >> 16);
+ double dbl = input;
+ int iPower = 6 - ((iExp * 19728) >> 16);
+ // iPower is between -22 and 35
if (iPower >= 0)
{
@@ -1755,29 +1584,29 @@ ReturnZero:
if (iPower > DEC_SCALE_MAX)
iPower = DEC_SCALE_MAX;
- dbl = dbl * s_doublePowers10[iPower];
+ dbl *= s_doublePowers10[iPower];
}
else
{
if (iPower != -1 || dbl >= 1E7)
- dbl = dbl / GetDoublePower10(-iPower);
+ dbl /= s_doublePowers10[-iPower];
else
iPower = 0; // didn't scale it
}
- System.Diagnostics.Debug.Assert(dbl < 1E7);
+ Debug.Assert(dbl < 1E7);
if (dbl < 1E6 && iPower < DEC_SCALE_MAX)
{
dbl *= 10;
iPower++;
- System.Diagnostics.Debug.Assert(dbl >= 1E6);
+ Debug.Assert(dbl >= 1E6);
}
// Round to integer
//
- ulMant = (uint)dbl;
+ uint ulMant = (uint)dbl;
dbl -= (double)ulMant; // difference between input & integer
- if (dbl > 0.5 || (dbl == 0.5) && (ulMant & 1) != 0)
+ if (dbl > 0.5 || dbl == 0.5 && (ulMant & 1) != 0)
ulMant++;
if (ulMant == 0)
@@ -1791,7 +1620,6 @@ ReturnZero:
if (iPower < 10)
{
pdecOut.Low64 = UInt32x32To64(ulMant, s_powers10[iPower]);
- pdecOut.High = 0;
}
else
{
@@ -1799,27 +1627,26 @@ ReturnZero:
//
if (iPower > 18)
{
- sdlLo.int64 = UInt32x32To64(ulMant, s_powers10[iPower - 18]);
- ulong tmplong;
- sdlLo.int64 = UInt64x64To128(sdlLo, s_tenToPowerEighteen, out tmplong);
- sdlHi.int64 = tmplong;
-
- if (sdlHi.High32 != 0)
- throw new OverflowException(SR.Overflow_Decimal);
+ ulong low64 = UInt32x32To64(ulMant, s_powers10[iPower - 18]);
+ low64 = UInt64x64To128(low64, TenToPowerEighteen, out ulong tmplong);
+ ulong hi64 = tmplong;
+ if (hi64 > uint.MaxValue)
+ goto ThrowOverflow;
+ pdecOut.Low64 = low64;
+ pdecOut.High = (uint)hi64;
}
else
{
- sdlLo.int64 = UInt32x32To64(ulMant, s_powers10[iPower - 9]);
- sdlHi.int64 = UInt32x32To64(TenToPowerNine, sdlLo.High32);
- sdlLo.int64 = UInt32x32To64(TenToPowerNine, sdlLo.Low32);
- sdlHi.int64 += sdlLo.High32;
- sdlLo.High32 = sdlHi.Low32;
- sdlHi.Low32 = sdlHi.High32;
+ ulong low64 = UInt32x32To64(ulMant, s_powers10[iPower - 9]);
+ ulong hi64 = UInt32x32To64(TenToPowerNine, (uint)(low64 >> 32));
+ low64 = UInt32x32To64(TenToPowerNine, (uint)low64);
+ pdecOut.Low = (uint)low64;
+ hi64 += low64 >> 32;
+ pdecOut.Mid = (uint)hi64;
+ hi64 >>= 32;
+ pdecOut.High = (uint)hi64;
}
- pdecOut.Low64 = sdlLo.int64;
- pdecOut.High = sdlHi.Low32;
}
- pdecOut.Scale = 0;
}
else
{
@@ -1831,65 +1658,84 @@ ReturnZero:
// we can't scale by any more than the power we used to
// get the integer.
//
- // DivMod32by32 returns the quotient in Lo, the remainder in Hi.
- //
- lmax = iPower < 6 ? iPower : 6;
+ int lmax = iPower;
+ if (lmax > 6)
+ lmax = 6;
- // lmax is the largest power of 10 to try, lmax <= 6.
- // We'll try powers 4, 2, and 1 unless they're too big.
- //
- for (cur = 4; cur > 0; cur >>= 1)
+ if ((ulMant & 0xF) == 0 && lmax >= 4)
{
- if (cur > lmax)
- continue;
+ const uint den = 10000;
+ uint div = ulMant / den;
+ if (ulMant == div * den)
+ {
+ ulMant = div;
+ iPower -= 4;
+ lmax -= 4;
+ }
+ }
- sdlLo.int64 = DivMod32by32(ulMant, s_powers10[cur]);
+ if ((ulMant & 3) == 0 && lmax >= 2)
+ {
+ const uint den = 100;
+ uint div = ulMant / den;
+ if (ulMant == div * den)
+ {
+ ulMant = div;
+ iPower -= 2;
+ lmax -= 2;
+ }
+ }
- if (sdlLo.High32 == 0)
+ if ((ulMant & 1) == 0 && lmax >= 1)
+ {
+ const uint den = 10;
+ uint div = ulMant / den;
+ if (ulMant == div * den)
{
- ulMant = sdlLo.Low32;
- iPower -= cur;
- lmax -= cur;
+ ulMant = div;
+ iPower--;
}
}
+
+ flags |= (uint)iPower << ScaleShift;
pdecOut.Low = ulMant;
- pdecOut.Mid = 0;
- pdecOut.High = 0;
- pdecOut.Scale = iPower;
}
- pdecOut.IsNegative = input < 0;
+ pdecOut.uflags = flags;
+ return;
+
+ThrowOverflow:
+ throw new OverflowException(SR.Overflow_Decimal);
}
//**********************************************************************
// VarDecFromR8 - Convert double to Decimal
//**********************************************************************
+#if PROJECTN // Workaround ProjectN bug #555233
+ [MethodImplAttribute(MethodImplOptions.NoOptimization)]
+#endif
internal static void VarDecFromR8(double input, out Decimal pdecOut)
{
- int iExp; // number of bits to left of binary point
- int iPower; // power-of-10 scale factor
- Split64 sdlMant = new Split64();
- Split64 sdlLo = new Split64();
- double dbl;
- int lmax, cur; // temps used during scale reduction
- uint ulPwrCur;
- uint ulQuo;
-
pdecOut = new Decimal();
// The most we can scale by is 10^28, which is just slightly more
// than 2^93. So a float with an exponent of -94 could just
// barely reach 0.5, but smaller exponents will always round to zero.
//
- iExp = (int)(GetExponent(input) - DBLBIAS);
+ const uint DBLBIAS = 1022;
+ int iExp = (int)(GetExponent(input) - DBLBIAS);
if (iExp < -94)
- return; // result should be zeroed out
+ return; // result should be zeroed out
if (iExp > 96)
- throw new OverflowException(SR.Overflow_Decimal);
- dbl = input;
- if (dbl < 0)
- dbl *= -1;
+ goto ThrowOverflow;
+
+ uint flags = 0;
+ if (input < 0)
+ {
+ input = -input;
+ flags = SignMask;
+ }
// Round the input to a 15-digit integer. The R8 format has
// only 15 digits of precision, and we want to keep garbage digits
@@ -1899,8 +1745,9 @@ ReturnZero:
// the exponent by log10(2). Using scaled integer multiplcation,
// log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
//
-
- iPower = 14 - ((iExp * 19728) >> 16);
+ double dbl = input;
+ int iPower = 14 - ((iExp * 19728) >> 16);
+ // iPower is between -14 and 43
if (iPower >= 0)
{
@@ -1909,32 +1756,32 @@ ReturnZero:
if (iPower > DEC_SCALE_MAX)
iPower = DEC_SCALE_MAX;
- dbl = dbl * s_doublePowers10[iPower];
+ dbl *= s_doublePowers10[iPower];
}
else
{
if (iPower != -1 || dbl >= 1E15)
- dbl = dbl / GetDoublePower10(-iPower);
+ dbl /= s_doublePowers10[-iPower];
else
iPower = 0; // didn't scale it
}
- System.Diagnostics.Debug.Assert(dbl < 1E15);
+ Debug.Assert(dbl < 1E15);
if (dbl < 1E14 && iPower < DEC_SCALE_MAX)
{
dbl *= 10;
iPower++;
- System.Diagnostics.Debug.Assert(dbl >= 1E14);
+ Debug.Assert(dbl >= 1E14);
}
// Round to int64
//
- sdlMant.int64 = (ulong)dbl;
- dbl -= (double)sdlMant.int64; // dif between input & integer
- if (dbl > 0.5 || dbl == 0.5 && (sdlMant.Low32 & 1) != 0)
- sdlMant.int64++;
+ ulong ulMant = (ulong)dbl;
+ dbl -= (double)ulMant; // difference between input & integer
+ if (dbl > 0.5 || dbl == 0.5 && (ulMant & 1) != 0)
+ ulMant++;
- if (sdlMant.int64 == 0)
+ if (ulMant == 0)
return; // result should be zeroed out
if (iPower < 0)
@@ -1944,27 +1791,27 @@ ReturnZero:
iPower = -iPower;
if (iPower < 10)
{
- sdlLo.int64 = UInt32x32To64(sdlMant.Low32, s_powers10[iPower]);
- sdlMant.int64 = UInt32x32To64(sdlMant.High32, s_powers10[iPower]);
- sdlMant.int64 += sdlLo.High32;
- sdlLo.High32 = sdlMant.Low32;
- sdlMant.Low32 = sdlMant.High32;
+ var pow10 = s_powers10[iPower];
+ ulong low64 = UInt32x32To64((uint)ulMant, pow10);
+ ulong hi64 = UInt32x32To64((uint)(ulMant >> 32), pow10);
+ pdecOut.Low = (uint)low64;
+ hi64 += low64 >> 32;
+ pdecOut.Mid = (uint)hi64;
+ hi64 >>= 32;
+ pdecOut.High = (uint)hi64;
}
else
{
// Have a big power of 10.
//
- System.Diagnostics.Debug.Assert(iPower <= 14);
- ulong tmpValue;
- sdlLo.int64 = UInt64x64To128(sdlMant, new Split64((ulong)s_doublePowers10[iPower]), out tmpValue);
- sdlMant.int64 = tmpValue;
-
- if (sdlMant.High32 != 0)
- throw new OverflowException(SR.Overflow_Decimal);
+ Debug.Assert(iPower <= 14);
+ ulong low64 = UInt64x64To128(ulMant, s_ulongPowers10[iPower - 1], out ulong tmplong);
+ ulong hi64 = tmplong;
+ if (hi64 > uint.MaxValue)
+ goto ThrowOverflow;
+ pdecOut.Low64 = low64;
+ pdecOut.High = (uint)hi64;
}
- pdecOut.Low64 = sdlLo.int64;
- pdecOut.High = sdlMant.Low32;
- pdecOut.Scale = 0;
}
else
{
@@ -1976,50 +1823,66 @@ ReturnZero:
// we can't scale by any more than the power we used to
// get the integer.
//
- // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
- //
- lmax = iPower < 14 ? iPower : 14;
+ int lmax = iPower;
+ if (lmax > 14)
+ lmax = 14;
- // lmax is the largest power of 10 to try, lmax <= 14.
- // We'll try powers 8, 4, 2, and 1 unless they're too big.
- //
- for (cur = 8; cur > 0; cur >>= 1)
+ if ((byte)ulMant == 0 && lmax >= 8)
{
- if (cur > lmax)
- continue;
-
- ulPwrCur = s_powers10[cur];
+ const uint den = 100000000;
+ ulong div = ulMant / den;
+ if (ulMant == div * den)
+ {
+ ulMant = div;
+ iPower -= 8;
+ lmax -= 8;
+ }
+ }
- if (sdlMant.High32 >= ulPwrCur)
+ if (((uint)ulMant & 0xF) == 0 && lmax >= 4)
+ {
+ const uint den = 10000;
+ ulong div = ulMant / den;
+ if (ulMant == div * den)
{
- // Overflow if we try to divide in one step.
- //
- sdlLo.int64 = DivMod64by32(sdlMant.High32, ulPwrCur);
- ulQuo = sdlLo.Low32;
- sdlLo.Low32 = sdlMant.Low32;
- sdlLo.int64 = DivMod64by32(sdlLo.int64, ulPwrCur);
+ ulMant = div;
+ iPower -= 4;
+ lmax -= 4;
}
- else
+ }
+
+ if (((uint)ulMant & 3) == 0 && lmax >= 2)
+ {
+ const uint den = 100;
+ ulong div = ulMant / den;
+ if (ulMant == div * den)
{
- ulQuo = 0;
- sdlLo.int64 = DivMod64by32(sdlMant.int64, ulPwrCur);
+ ulMant = div;
+ iPower -= 2;
+ lmax -= 2;
}
+ }
- if (sdlLo.High32 == 0)
+ if (((uint)ulMant & 1) == 0 && lmax >= 1)
+ {
+ const uint den = 10;
+ ulong div = ulMant / den;
+ if (ulMant == div * den)
{
- sdlMant.High32 = ulQuo;
- sdlMant.Low32 = sdlLo.Low32;
- iPower -= cur;
- lmax -= cur;
+ ulMant = div;
+ iPower--;
}
}
- pdecOut.High = 0;
- pdecOut.Scale = iPower;
- pdecOut.Low64 = sdlMant.int64;
+ flags |= (uint)iPower << ScaleShift;
+ pdecOut.Low64 = ulMant;
}
- pdecOut.IsNegative = input < 0;
+ pdecOut.uflags = flags;
+ return;
+
+ThrowOverflow:
+ throw new OverflowException(SR.Overflow_Decimal);
}
//**********************************************************************
@@ -2035,8 +1898,11 @@ ReturnZero:
//**********************************************************************
internal static double VarR8FromDec(ref Decimal pdecIn)
{
+ // Value taken via reverse engineering the double that corrisponds to 2^65. (oleaut32 has ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0))
+ const double ds2to64 = 1.8446744073709552e+019;
+
double dbl = ((double)pdecIn.Low64 +
- (double)pdecIn.High * ds2to64) / GetDoublePower10(pdecIn.Scale);
+ (double)pdecIn.High * ds2to64) / s_doublePowers10[pdecIn.Scale];
if (pdecIn.IsNegative)
dbl = -dbl;
@@ -2161,28 +2027,7 @@ ReturnZero:
if (ulTmp == 0)
ulTmp = d2.Mid;
- iCurScale = 1;
- if ((ulTmp & 0xFFFF0000) == 0)
- {
- ulTmp <<= 16;
- iCurScale += 16;
- }
- if ((ulTmp & 0xFF000000) == 0)
- {
- ulTmp <<= 8;
- iCurScale += 8;
- }
- if ((ulTmp & 0xF0000000) == 0)
- {
- ulTmp <<= 4;
- iCurScale += 4;
- }
- if ((ulTmp & 0xC0000000) == 0)
- {
- ulTmp <<= 2;
- iCurScale += 2;
- }
- iCurScale += (int)ulTmp >> 31;
+ iCurScale = LeadingZeroCount(ulTmp);
// Shift both dividend and divisor left by iCurScale.
//
@@ -2336,7 +2181,7 @@ Unscale:
// we can extract. We use this as a quick test on whether to try a
// given power.
//
- while (((uint)low64 & 0xFF) == 0 && iScale >= 8)
+ while ((byte)low64 == 0 && iScale >= 8)
{
if (Div96ByConst100000000(ref bufQuo) == 0)
{
@@ -2397,12 +2242,9 @@ Unscale:
RoundUp:
{
- if (++bufQuo.Low64 == 0)
+ if (++bufQuo.Low64 == 0 && ++bufQuo.U2 == 0)
{
- if (++bufQuo.U2 == 0)
- {
- iScale = OverflowUnscale(ref bufQuo, iScale, true);
- }
+ iScale = OverflowUnscale(ref bufQuo, iScale, true);
}
goto Unscale;
}
@@ -2412,91 +2254,6 @@ ThrowOverflow:
}
//**********************************************************************
- // VarDecInt - Decimal Int (round down to integer)
- //**********************************************************************
- internal static void VarDecInt(ref Decimal d)
- {
- Decimal result = new Decimal();
-
- if (DecCalc.DecFixInt(ref d, ref result) != 0 && result.IsNegative)
- // We have chopped off a non-zero amount from a negative value. Since
- // we round toward -infinity, we must increase the integer result by
- // 1 to make it more negative. This will never overflow because
- // in order to have a remainder, we must have had a non-zero scale factor.
- // Our scale factor is back to zero now.
- //
- if (++result.Low64 == 0)
- result.High++;
-
- d = result;
- }
-
- //**********************************************************************
- // VarDecFix - Decimal Fix (chop to integer)
- //**********************************************************************
- internal static void VarDecFix(ref Decimal d)
- {
- Decimal result = new Decimal();
- DecFixInt(ref d, ref result);
- d = result;
- }
-
- //**********************************************************************
- // VarDecRound - Decimal Round
- //**********************************************************************
- internal static unsafe void VarDecRound(ref Decimal input, int decimals, ref Decimal result)
- {
- Buf12 bufNum;
- _ = &bufNum; // workaround for CS0165
- uint remainder;
- uint sticky;
- uint power;
- int scale;
-
- System.Diagnostics.Debug.Assert(decimals >= 0);
-
- scale = input.Scale - decimals;
- if (scale > 0)
- {
- bufNum.U0 = input.ulo;
- bufNum.U1 = input.umid;
- bufNum.U2 = input.uhi;
- result.IsNegative = input.IsNegative;
- remainder = sticky = 0;
-
- do
- {
- sticky |= remainder;
- if (scale > MaxInt32Scale)
- power = TenToPowerNine;
- else
- power = s_powers10[scale];
-
- remainder = Div96By32(ref bufNum, power);
- scale -= MaxInt32Scale;
- } while (scale > 0);
-
- // Now round. ulRem has last remainder, ulSticky has sticky bits.
- // To do IEEE rounding, we add LSB of result to sticky bits so
- // either causes round up if remainder * 2 == last divisor.
- sticky |= bufNum.U0 & 1;
- remainder = (remainder << 1) + (uint)(sticky != 0 ? 1 : 0);
- if (power < remainder
- && ++bufNum.U0 == 0
- && ++bufNum.U1 == 0)
- ++bufNum.U2;
-
- result.ulo = bufNum.U0;
- result.umid = bufNum.U1;
- result.uhi = bufNum.U2;
- result.Scale = decimals;
- return;
- }
-
- result = input;
- }
-
- //**********************************************************************
// VarDecMod - Computes the remainder between two decimals
//**********************************************************************
internal static Decimal VarDecMod(ref Decimal d1, ref Decimal d2)
@@ -2510,7 +2267,7 @@ ThrowOverflow:
// This piece of code is to work around the fact that Dividing a decimal with 28 digits number by decimal which causes
// causes the result to be 28 digits, can cause to be incorrectly rounded up.
// eg. Decimal.MaxValue / 2 * Decimal.MaxValue will overflow since the division by 2 was rounded instead of being truncked.
- if (Abs(d1) < Abs(d2))
+ if (Math.Abs(d1) < Math.Abs(d2))
{
return d1;
}
@@ -2547,91 +2304,137 @@ ThrowOverflow:
return result;
}
+ internal enum RoundingMode
+ {
+ ToEven = 0,
+ AwayFromZero = 1,
+ Truncate = 2,
+ Floor = 3,
+ Ceiling = 4,
+ }
- // This method does a 'raw' and 'unchecked' addition of a UInt32 to a Decimal in place.
- // 'raw' means that it operates on the internal 96-bit unsigned integer value and
- // ingores the sign and scale. This means that it is not equivalent to just adding
- // that number, as the sign and scale are effectively applied to the UInt32 value also.
- // 'unchecked' means that it does not fail if you overflow the 96 bit value.
- private static void InternalAddUInt32RawUnchecked(ref Decimal value, UInt32 i)
+ // Does an in-place round by the specified scale
+ internal static void InternalRound(ref Decimal d, uint scale, RoundingMode mode)
{
- UInt32 v;
- UInt32 sum;
- v = value.ulo;
- sum = v + i;
- value.ulo = sum;
- if (sum < v || sum < i)
- {
- v = value.umid;
- sum = v + 1;
- value.umid = sum;
- if (sum < v || sum < 1)
+ // the scale becomes the desired decimal count
+ d.uflags -= scale << ScaleShift;
+
+ uint remainder, sticky = 0, power;
+ // First divide the value by constant 10^9 up to three times
+ while (scale >= MaxInt32Scale)
+ {
+ scale -= MaxInt32Scale;
+
+ const uint divisor = TenToPowerNine;
+ uint n = d.uhi;
+ if (n == 0)
{
- value.uhi = value.uhi + 1;
+ ulong tmp = d.Low64;
+ ulong div = tmp / divisor;
+ d.Low64 = div;
+ remainder = (uint)(tmp - div * divisor);
}
+ else
+ {
+ uint q;
+ d.uhi = q = n / divisor;
+ remainder = n - q * divisor;
+ n = d.umid;
+ if ((n | remainder) != 0)
+ {
+ d.umid = q = (uint)((((ulong)remainder << 32) | n) / divisor);
+ remainder = n - q * divisor;
+ }
+ n = d.ulo;
+ if ((n | remainder) != 0)
+ {
+ d.ulo = q = (uint)((((ulong)remainder << 32) | n) / divisor);
+ remainder = n - q * divisor;
+ }
+ }
+ power = divisor;
+ if (scale == 0)
+ goto checkRemainder;
+ sticky |= remainder;
}
- }
- // This method does an in-place division of a decimal by a UInt32, returning the remainder.
- // Although it does not operate on the sign or scale, this does not result in any
- // caveat for the result. It is equivalent to dividing by that number.
- private static UInt32 InternalDivRemUInt32(ref Decimal value, UInt32 divisor)
- {
- UInt32 remainder = 0;
- UInt64 n;
- if (value.uhi != 0)
{
- n = value.uhi;
- value.uhi = (UInt32)(n / divisor);
- remainder = (UInt32)(n % divisor);
+ power = s_powers10[scale];
+ // TODO: https://github.com/dotnet/coreclr/issues/3439
+ uint n = d.uhi;
+ if (n == 0)
+ {
+ ulong tmp = d.Low64;
+ if (tmp == 0)
+ {
+ if (mode <= RoundingMode.Truncate)
+ goto done;
+ remainder = 0;
+ goto checkRemainder;
+ }
+ ulong div = tmp / power;
+ d.Low64 = div;
+ remainder = (uint)(tmp - div * power);
+ }
+ else
+ {
+ uint q;
+ d.uhi = q = n / power;
+ remainder = n - q * power;
+ n = d.umid;
+ if ((n | remainder) != 0)
+ {
+ d.umid = q = (uint)((((ulong)remainder << 32) | n) / power);
+ remainder = n - q * power;
+ }
+ n = d.ulo;
+ if ((n | remainder) != 0)
+ {
+ d.ulo = q = (uint)((((ulong)remainder << 32) | n) / power);
+ remainder = n - q * power;
+ }
+ }
}
- if (value.umid != 0 || remainder != 0)
+
+checkRemainder:
+ if (mode == RoundingMode.Truncate)
+ goto done;
+ else if (mode == RoundingMode.ToEven)
{
- n = ((UInt64)remainder << 32) | value.umid;
- value.umid = (UInt32)(n / divisor);
- remainder = (UInt32)(n % divisor);
+ // To do IEEE rounding, we add LSB of result to sticky bits so either causes round up if remainder * 2 == last divisor.
+ remainder <<= 1;
+ if ((sticky | d.ulo & 1) != 0)
+ remainder++;
+ if (power >= remainder)
+ goto done;
}
- if (value.ulo != 0 || remainder != 0)
+ else if (mode == RoundingMode.AwayFromZero)
{
- n = ((UInt64)remainder << 32) | value.ulo;
- value.ulo = (UInt32)(n / divisor);
- remainder = (UInt32)(n % divisor);
+ // Round away from zero at the mid point.
+ remainder <<= 1;
+ if (power > remainder)
+ goto done;
}
- return remainder;
- }
-
- // Does an in-place round the specified number of digits, rounding mid-point values
- // away from zero
- internal static void InternalRoundFromZero(ref Decimal d, int decimalCount)
- {
- Int32 scale = d.Scale;
- Int32 scaleDifference = scale - decimalCount;
- if (scaleDifference <= 0)
+ else if (mode == RoundingMode.Floor)
{
- return;
+ // Round toward -infinity if we have chopped off a non-zero amount from a negative value.
+ if ((remainder | sticky) == 0 || !d.IsNegative)
+ goto done;
}
- // Divide the value by 10^scaleDifference
- UInt32 lastRemainder;
- UInt32 lastDivisor;
- do
- {
- Int32 diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference;
- lastDivisor = s_powers10[diffChunk];
- lastRemainder = InternalDivRemUInt32(ref d, lastDivisor);
- scaleDifference -= diffChunk;
- } while (scaleDifference > 0);
-
- // Round away from zero at the mid point
- if (lastRemainder >= (lastDivisor >> 1))
+ else
{
- InternalAddUInt32RawUnchecked(ref d, 1);
+ Debug.Assert(mode == RoundingMode.Ceiling);
+ // Round toward infinity if we have chopped off a non-zero amount from a positive value.
+ if ((remainder | sticky) == 0 || d.IsNegative)
+ goto done;
}
-
- // the scale becomes the desired decimal count
- d.Scale = decimalCount;
+ if (++d.Low64 == 0)
+ d.uhi++;
+done:
+ return;
}
- #region Number Formatting helpers
+#region Number Formatting helpers
private static uint D32DivMod1E9(uint hi32, ref uint lo32)
{
@@ -2697,29 +2500,7 @@ ThrowOverflow:
D32AddCarry(ref value.uhi, d.High);
}
- #endregion
-
- private struct Split64
- {
- internal ulong int64;
-
- public Split64(ulong value)
- {
- int64 = value;
- }
-
- public uint Low32
- {
- get { return (uint)int64; }
- set { int64 = (int64 & 0xffffffff00000000) | value; }
- }
-
- public uint High32
- {
- get { return (uint)(int64 >> 32); }
- set { int64 = (int64 & 0x00000000ffffffff) | ((ulong)value << 32); }
- }
- }
+#endregion
struct PowerOvfl
{
diff --git a/src/System.Private.CoreLib/src/System/Decimal.cs b/src/System.Private.CoreLib/src/System/Decimal.cs
index e9498a529..244ef6223 100644
--- a/src/System.Private.CoreLib/src/System/Decimal.cs
+++ b/src/System.Private.CoreLib/src/System/Decimal.cs
@@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
@@ -247,9 +248,7 @@ namespace System
public static long ToOACurrency(Decimal value)
{
- long cy;
- DecCalc.VarCyFromDec(ref value, out cy);
- return cy;
+ return DecCalc.VarCyFromDec(ref value);
}
private static bool IsValid(uint flags) => (flags & ~(SignMask | ScaleMask)) == 0 && ((flags & ScaleMask) <= (28 << 16));
@@ -275,11 +274,6 @@ namespace System
//
public Decimal(int[] bits)
{
- SetBits(bits);
- }
-
- private void SetBits(int[] bits)
- {
if (bits == null)
throw new ArgumentNullException(nameof(bits));
if (bits.Length == 4)
@@ -315,37 +309,25 @@ namespace System
{
// OnDeserialization is called after each instance of this class is deserialized.
// This callback method performs decimal validation after being deserialized.
- try
- {
- SetBits(GetBits(this));
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Overflow_Decimal, e);
- }
+ if (!IsValid(uflags))
+ throw new SerializationException(SR.Overflow_Decimal);
}
// Constructs a Decimal from its constituent parts.
- private Decimal(int lo, int mid, int hi, int flags)
+ private Decimal(ulong ulomidLE, uint hi, uint flags)
{
- if ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16))
- {
- this.lo = lo;
- this.mid = mid;
- this.hi = hi;
- this.flags = flags;
- return;
- }
- throw new ArgumentException(SR.Arg_DecBitCtor);
+ this.ulomidLE = ulomidLE;
+ this.uhi = hi;
+ this.uflags = flags;
}
// Returns the absolute value of the given Decimal. If d is
// positive, the result is d. If d is negative, the result
// is -d.
//
- internal static Decimal Abs(Decimal d)
+ internal static Decimal Abs(ref Decimal d)
{
- return new Decimal(d.lo, d.mid, d.hi, (int)(d.uflags & ~SignMask));
+ return new Decimal(d.ulomidLE, d.uhi, d.uflags & ~SignMask);
}
@@ -362,7 +344,10 @@ namespace System
// towards positive infinity.
public static Decimal Ceiling(Decimal d)
{
- return (-(Decimal.Floor(-d)));
+ uint flags = d.uflags;
+ if ((flags & ScaleMask) != 0)
+ DecCalc.InternalRound(ref d, (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Ceiling);
+ return d;
}
// Compares two Decimal values, returning an integer that indicates their
@@ -457,7 +442,9 @@ namespace System
//
public static Decimal Floor(Decimal d)
{
- DecCalc.VarDecInt(ref d);
+ uint flags = d.uflags;
+ if ((flags & ScaleMask) != 0)
+ DecCalc.InternalRound(ref d, (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Floor);
return d;
}
@@ -544,7 +531,7 @@ namespace System
return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider));
}
- public static Decimal Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider)
+ public static Decimal Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
{
ValidateParseStyleFloatingPoint(style);
return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider));
@@ -624,16 +611,16 @@ namespace System
// Returns the larger of two Decimal values.
//
- internal static Decimal Max(Decimal d1, Decimal d2)
+ internal static ref Decimal Max(ref Decimal d1, ref Decimal d2)
{
- return Compare(d1, d2) >= 0 ? d1 : d2;
+ return ref DecCalc.VarDecCmp(ref d1, ref d2) >= 0 ? ref d1 : ref d2;
}
// Returns the smaller of two Decimal values.
//
- internal static Decimal Min(Decimal d1, Decimal d2)
+ internal static ref Decimal Min(ref Decimal d1, ref Decimal d2)
{
- return Compare(d1, d2) < 0 ? d1 : d2;
+ return ref DecCalc.VarDecCmp(ref d1, ref d2) < 0 ? ref d1 : ref d2;
}
@@ -655,7 +642,7 @@ namespace System
//
public static Decimal Negate(Decimal d)
{
- return new Decimal(d.lo, d.mid, d.hi, (int)(d.uflags ^ SignMask));
+ return new Decimal(d.ulomidLE, d.uhi, d.uflags ^ SignMask);
}
// Rounds a Decimal value to a given number of decimal places. The value
@@ -666,46 +653,21 @@ namespace System
// By default a mid-point value is rounded to the nearest even number. If the mode is
// passed in, it can also round away from zero.
- public static Decimal Round(Decimal d)
- {
- return Round(d, 0);
- }
+ public static Decimal Round(Decimal d) => Round(ref d, 0, MidpointRounding.ToEven);
+ public static Decimal Round(Decimal d, int decimals) => Round(ref d, decimals, MidpointRounding.ToEven);
+ public static Decimal Round(Decimal d, MidpointRounding mode) => Round(ref d, 0, mode);
+ public static Decimal Round(Decimal d, int decimals, MidpointRounding mode) => Round(ref d, decimals, mode);
- public static Decimal Round(Decimal d, int decimals)
+ private static Decimal Round(ref Decimal d, int decimals, MidpointRounding mode)
{
- Decimal result = new Decimal();
-
- if (decimals < 0 || decimals > 28)
+ if ((uint)decimals > 28)
throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound);
+ if ((uint)mode > (uint)MidpointRounding.AwayFromZero)
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
- DecCalc.VarDecRound(ref d, decimals, ref result);
-
- d = result;
- return d;
- }
-
- public static Decimal Round(Decimal d, MidpointRounding mode)
- {
- return Round(d, 0, mode);
- }
-
- public static Decimal Round(Decimal d, int decimals, MidpointRounding mode)
- {
- if (decimals < 0 || decimals > 28)
- throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound);
- if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero)
- throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, "MidpointRounding"), nameof(mode));
-
- if (mode == MidpointRounding.ToEven)
- {
- Decimal result = new Decimal();
- DecCalc.VarDecRound(ref d, decimals, ref result);
- d = result;
- }
- else
- {
- DecCalc.InternalRoundFromZero(ref d, decimals);
- }
+ int scale = d.Scale - decimals;
+ if (scale > 0)
+ DecCalc.InternalRound(ref d, (uint)scale, (DecCalc.RoundingMode)mode);
return d;
}
@@ -791,8 +753,8 @@ namespace System
//
public static int ToInt32(Decimal d)
{
- if (d.Scale != 0) DecCalc.VarDecFix(ref d);
- if (d.hi == 0 && d.mid == 0)
+ Truncate(ref d);
+ if ((d.hi | d.mid) == 0)
{
int i = d.lo;
if (!d.IsNegative)
@@ -814,10 +776,10 @@ namespace System
//
public static long ToInt64(Decimal d)
{
- if (d.Scale != 0) DecCalc.VarDecFix(ref d);
+ Truncate(ref d);
if (d.uhi == 0)
{
- long l = d.ulo | (long)(int)d.umid << 32;
+ long l = (long)d.Low64;
if (!d.IsNegative)
{
if (l >= 0) return l;
@@ -858,11 +820,12 @@ namespace System
[CLSCompliant(false)]
public static uint ToUInt32(Decimal d)
{
- if (d.Scale != 0) DecCalc.VarDecFix(ref d);
- if (d.uhi == 0 && d.umid == 0)
+ Truncate(ref d);
+ if ((d.uhi | d.umid) == 0)
{
- if (!d.IsNegative || d.ulo == 0)
- return d.ulo;
+ uint i = d.ulo;
+ if (!d.IsNegative || i == 0)
+ return i;
}
throw new OverflowException(SR.Overflow_UInt32);
}
@@ -874,10 +837,10 @@ namespace System
[CLSCompliant(false)]
public static ulong ToUInt64(Decimal d)
{
- if (d.Scale != 0) DecCalc.VarDecFix(ref d);
+ Truncate(ref d);
if (d.uhi == 0)
{
- ulong l = (ulong)d.ulo | ((ulong)d.umid << 32);
+ ulong l = d.Low64;
if (!d.IsNegative || l == 0)
return l;
}
@@ -898,10 +861,18 @@ namespace System
//
public static Decimal Truncate(Decimal d)
{
- DecCalc.VarDecFix(ref d);
+ Truncate(ref d);
return d;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Truncate(ref Decimal d)
+ {
+ uint flags = d.uflags;
+ if ((flags & ScaleMask) != 0)
+ DecCalc.InternalRound(ref d, (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Truncate);
+ }
+
public static implicit operator Decimal(byte value)
{
return new Decimal(value);
diff --git a/src/System.Private.CoreLib/src/System/Delegate.cs b/src/System.Private.CoreLib/src/System/Delegate.cs
index 0ec444eaa..e4746b1ab 100644
--- a/src/System.Private.CoreLib/src/System/Delegate.cs
+++ b/src/System.Private.CoreLib/src/System/Delegate.cs
@@ -493,21 +493,21 @@ namespace System
// This method will combine this delegate with the passed delegate
// to form a new delegate.
- protected virtual Delegate CombineImpl(Delegate follow)
+ protected virtual Delegate CombineImpl(Delegate d)
{
- if ((Object)follow == null) // cast to object for a more efficient test
+ if ((Object)d == null) // cast to object for a more efficient test
return this;
// Verify that the types are the same...
- if (!InternalEqualTypes(this, follow))
+ if (!InternalEqualTypes(this, d))
throw new ArgumentException();
- if (IsDynamicDelegate() && follow.IsDynamicDelegate())
+ if (IsDynamicDelegate() && d.IsDynamicDelegate())
{
throw new InvalidOperationException();
}
- MulticastDelegate dFollow = (MulticastDelegate)follow;
+ MulticastDelegate dFollow = (MulticastDelegate)d;
Delegate[] resultList;
int followCount = 1;
Delegate[] followList = dFollow.m_helperObject as Delegate[];
@@ -616,12 +616,12 @@ namespace System
// look at the invocation list.) If this is found we remove it from
// this list and return a new delegate. If its not found a copy of the
// current list is returned.
- protected virtual Delegate RemoveImpl(Delegate value)
+ protected virtual Delegate RemoveImpl(Delegate d)
{
// There is a special case were we are removing using a delegate as
// the value we need to check for this case
//
- MulticastDelegate v = value as MulticastDelegate;
+ MulticastDelegate v = d as MulticastDelegate;
if (v == null)
return this;
@@ -631,7 +631,7 @@ namespace System
if (invocationList == null)
{
// they are both not real Multicast
- if (this.Equals(value))
+ if (this.Equals(d))
return null;
}
else
@@ -639,7 +639,7 @@ namespace System
int invocationCount = (int)m_extraFunctionPointerOrData;
for (int i = invocationCount; --i >= 0;)
{
- if (value.Equals(invocationList[i]))
+ if (d.Equals(invocationList[i]))
{
if (invocationCount == 2)
{
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs
index aae5c6c37..9493cadef 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs
@@ -11,8 +11,9 @@ namespace System.Diagnostics
{
public static partial class Debug
{
- private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+ private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
{
+ // We can safely ignore errorSource since it's a CoreCLR specific argument for distinguishing calls from Debug.Assert and Environment.FailFast
string fullMessage = message + Environment.NewLine + detailMessage;
bool result = DeveloperExperience.Default.OnContractFailure(stackTrace, ContractFailureKind.Assert, fullMessage, null, null, null);
if (!result)
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs b/src/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs
index 7c4d87754..413000327 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs
@@ -7,6 +7,7 @@ namespace System.Diagnostics
/// <summary>
/// Annotations used by debugger
/// </summary>
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public static class DebugAnnotations
{
/// <summary>
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs b/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs
index ff6219b79..68bf18577 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs
@@ -4,6 +4,7 @@
namespace System.Diagnostics
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)]
public sealed class DebuggerGuidedStepThroughAttribute : Attribute
{
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/LowLevelDebugFuncEval.cs b/src/System.Private.CoreLib/src/System/Diagnostics/LowLevelDebugFuncEval.cs
new file mode 100644
index 000000000..f1d140429
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/LowLevelDebugFuncEval.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics
+{
+ public static class LowLevelDebugFuncEval
+ {
+ private static Action s_highLevelDebugFuncEvalHelper;
+ private static Action<long> s_highLevelDebugFuncEvalAbortHelper;
+
+ [RuntimeExport("DebugFuncEvalHelper")]
+ public static void DebugFuncEvalHelper()
+ {
+ Debug.Assert(s_highLevelDebugFuncEvalHelper != null);
+ s_highLevelDebugFuncEvalHelper();
+ }
+
+ [NativeCallable(EntryPoint="DebugFuncEvalAbortHelper")]
+ public static void DebugFuncEvalAbortHelper(long pointerFromDebugger)
+ {
+ Debug.Assert(s_highLevelDebugFuncEvalHelper != null);
+ s_highLevelDebugFuncEvalAbortHelper(pointerFromDebugger);
+ }
+
+ public static void SetHighLevelDebugFuncEvalHelper(Action highLevelDebugFuncEvalHelper)
+ {
+ s_highLevelDebugFuncEvalHelper = highLevelDebugFuncEvalHelper;
+ }
+
+ public static void SetHighLevelDebugFuncEvalAbortHelper(Action<long> highLevelDebugFuncEvalAbortHelper)
+ {
+ s_highLevelDebugFuncEvalAbortHelper = highLevelDebugFuncEvalAbortHelper;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs
index 7dc82a590..4d5a2155c 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs
@@ -5,6 +5,7 @@
using System;
using System.Runtime;
using System.Text;
+using System.Reflection;
using Internal.Diagnostics;
@@ -202,6 +203,19 @@ namespace System.Diagnostics
/// </summary>
public override String ToString()
{
+ return ToString(TraceFormat.Normal); // default behavior in RT did not have trailing newline
+ }
+
+ // TraceFormat is Used to specify options for how the
+ // string-representation of a StackTrace should be generated.
+ internal enum TraceFormat
+ {
+ Normal,
+ TrailingNewLine, // include a trailing new line character
+ }
+
+ internal String ToString(TraceFormat traceFormat)
+ {
if (_stackFrames == null)
{
return "";
@@ -212,7 +226,14 @@ namespace System.Diagnostics
{
frame.AppendToStackTrace(builder);
}
+
+ if (traceFormat == TraceFormat.TrailingNewLine)
+ {
+ builder.Append(Environment.NewLine);
+ }
+
return builder.ToString();
}
+
}
}
diff --git a/src/System.Private.CoreLib/src/System/Enum.cs b/src/System.Private.CoreLib/src/System/Enum.cs
index e863605c9..06da1b117 100644
--- a/src/System.Private.CoreLib/src/System/Enum.cs
+++ b/src/System.Private.CoreLib/src/System/Enum.cs
@@ -833,6 +833,7 @@ namespace System
return Format(enumInfo, this, format);
}
+ [Obsolete("The provider argument is not used. Please use ToString().")]
public String ToString(String format, IFormatProvider provider)
{
return ToString(format);
diff --git a/src/System.Private.CoreLib/src/System/Environment.Win32.cs b/src/System.Private.CoreLib/src/System/Environment.Win32.cs
new file mode 100644
index 000000000..4d6dd60db
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Environment.Win32.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+
+namespace System
+{
+ internal static partial class Environment
+ {
+ internal static string SystemDirectory
+ {
+ get
+ {
+ StringBuilder sb = new StringBuilder(Interop.Kernel32.MAX_PATH);
+ int r = Interop.mincore.GetSystemDirectory(sb, Interop.Kernel32.MAX_PATH);
+ return sb.ToString();
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Environment.cs b/src/System.Private.CoreLib/src/System/Environment.cs
index 9835a2592..c1d4ac171 100644
--- a/src/System.Private.CoreLib/src/System/Environment.cs
+++ b/src/System.Private.CoreLib/src/System/Environment.cs
@@ -13,10 +13,12 @@
============================================================*/
using System.Diagnostics;
+using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;
using Internal.Runtime.Augments;
+using Internal.DeveloperExperience;
namespace System
{
@@ -58,6 +60,17 @@ namespace System
RuntimeExceptionHelpers.FailFast(message, exception);
}
+ internal static void FailFast(String message, Exception exception, String errorSource)
+ {
+ // TODO: errorSource originates from CoreCLR (See: https://github.com/dotnet/coreclr/pull/15895)
+ // For now, we ignore errorSource on CoreRT but we should distinguish the way FailFast prints exception message using errorSource
+ bool result = DeveloperExperience.Default.OnContractFailure(exception.StackTrace, ContractFailureKind.Assert, message, null, null, null);
+ if (!result)
+ {
+ RuntimeExceptionHelpers.FailFast(message, exception);
+ }
+ }
+
// Still needed by shared\System\Diagnostics\Debug.Unix.cs
public static string GetEnvironmentVariable(string variable) => EnvironmentAugments.GetEnvironmentVariable(variable);
diff --git a/src/System.Private.CoreLib/src/System/GC.cs b/src/System.Private.CoreLib/src/System/GC.cs
index 053785d75..68d1bc9a3 100644
--- a/src/System.Private.CoreLib/src/System/GC.cs
+++ b/src/System.Private.CoreLib/src/System/GC.cs
@@ -75,23 +75,23 @@ namespace System
/// Returns the current generation number of the target
/// of a specified <see cref="System.WeakReference"/>.
/// </summary>
- /// <param name="wr">The WeakReference whose target is the object
+ /// <param name="wo">The WeakReference whose target is the object
/// whose generation will be returned</param>
/// <returns>The generation of the target of the WeakReference</returns>
/// <exception cref="ArgumentNullException">The target of the weak reference
/// has already been garbage collected.</exception>
- public static int GetGeneration(WeakReference wr)
+ public static int GetGeneration(WeakReference wo)
{
// note - this throws an NRE if given a null weak reference. This isn't
// documented, but it's the behavior of Desktop and CoreCLR.
- Object handleRef = RuntimeImports.RhHandleGet(wr.m_handle);
+ Object handleRef = RuntimeImports.RhHandleGet(wo.m_handle);
if (handleRef == null)
{
- throw new ArgumentNullException(nameof(wr));
+ throw new ArgumentNullException(nameof(wo));
}
int result = RuntimeImports.RhGetGeneration(handleRef);
- KeepAlive(wr);
+ KeepAlive(wo);
return result;
}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs
index cbf3fb555..40f0ab008 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs
@@ -12,7 +12,7 @@ namespace System.Globalization
public partial class CompareInfo
{
[NonSerialized]
- private Interop.GlobalizationInterop.SafeSortHandle _sortHandle;
+ private Interop.Globalization.SafeSortHandle _sortHandle;
[NonSerialized]
private bool _isAsciiEqualityOrdinal;
@@ -27,12 +27,12 @@ namespace System.Globalization
}
else
{
- Interop.GlobalizationInterop.ResultCode resultCode = Interop.GlobalizationInterop.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle);
- if (resultCode != Interop.GlobalizationInterop.ResultCode.Success)
+ Interop.Globalization.ResultCode resultCode = Interop.Globalization.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle);
+ if (resultCode != Interop.Globalization.ResultCode.Success)
{
_sortHandle.Dispose();
- if (resultCode == Interop.GlobalizationInterop.ResultCode.OutOfMemory)
+ if (resultCode == Interop.Globalization.ResultCode.OutOfMemory)
throw new OutOfMemoryException();
throw new ExternalException(SR.Arg_ExternalException);
@@ -62,7 +62,7 @@ namespace System.Globalization
{
fixed (char* pSource = source)
{
- int index = Interop.GlobalizationInterop.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + startIndex, count, findLast: false);
+ int index = Interop.Globalization.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + startIndex, count, findLast: false);
return index != -1 ?
startIndex + index :
-1;
@@ -87,6 +87,47 @@ namespace System.Globalization
return -1;
}
+ internal static unsafe int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ Debug.Assert(source.Length != 0);
+ Debug.Assert(value.Length != 0);
+
+ if (source.Length < value.Length)
+ {
+ return -1;
+ }
+
+ if (ignoreCase)
+ {
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pValue = &MemoryMarshal.GetReference(value))
+ {
+ int index = Interop.Globalization.IndexOfOrdinalIgnoreCase(pValue, value.Length, pSource, source.Length, findLast: false);
+ return index;
+ }
+ }
+
+ int endIndex = source.Length - value.Length;
+ for (int i = 0; i <= endIndex; i++)
+ {
+ int valueIndex, sourceIndex;
+
+ for (valueIndex = 0, sourceIndex = i;
+ valueIndex < value.Length && source[sourceIndex] == value[valueIndex];
+ valueIndex++, sourceIndex++)
+ ;
+
+ if (valueIndex == value.Length)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
{
Debug.Assert(!GlobalizationMode.Invariant);
@@ -113,7 +154,7 @@ namespace System.Globalization
{
fixed (char* pSource = source)
{
- int lastIndex = Interop.GlobalizationInterop.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + leftStartIndex, count, findLast: true);
+ int lastIndex = Interop.Globalization.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + leftStartIndex, count, findLast: true);
return lastIndex != -1 ?
leftStartIndex + lastIndex :
-1;
@@ -140,7 +181,7 @@ namespace System.Globalization
{
Debug.Assert(!GlobalizationMode.Invariant);
- return Interop.GlobalizationInterop.CompareStringOrdinalIgnoreCase(string1, count1, string2, count2);
+ return Interop.Globalization.CompareStringOrdinalIgnoreCase(string1, count1, string2, count2);
}
// TODO https://github.com/dotnet/coreclr/issues/13827:
@@ -155,7 +196,7 @@ namespace System.Globalization
fixed (char* pString1 = &MemoryMarshal.GetReference(string1))
fixed (char* pString2 = &string2.GetRawStringData())
{
- return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options);
+ return Interop.Globalization.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options);
}
}
@@ -167,7 +208,7 @@ namespace System.Globalization
fixed (char* pString1 = &MemoryMarshal.GetReference(string1))
fixed (char* pString2 = &MemoryMarshal.GetReference(string2))
{
- return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options);
+ return Interop.Globalization.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options);
}
}
@@ -212,12 +253,165 @@ namespace System.Globalization
#endif
fixed (char* pSource = source)
{
- index = Interop.GlobalizationInterop.IndexOf(_sortHandle, target, target.Length, pSource + startIndex, count, options, matchLengthPtr);
+ index = Interop.Globalization.IndexOf(_sortHandle, target, target.Length, pSource + startIndex, count, options, matchLengthPtr);
return index != -1 ? index + startIndex : -1;
}
}
+ // For now, this method is only called from Span APIs with either options == CompareOptions.None or CompareOptions.IgnoreCase
+ internal unsafe int IndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(source.Length != 0);
+ Debug.Assert(target.Length != 0);
+
+ if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options))
+ {
+ if ((options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase)
+ {
+ return IndexOfOrdinalIgnoreCaseHelper(source, target, options, matchLengthPtr);
+ }
+ else
+ {
+ return IndexOfOrdinalHelper(source, target, options, matchLengthPtr);
+ }
+ }
+ else
+ {
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pTarget = &MemoryMarshal.GetReference(target))
+ {
+ return Interop.Globalization.IndexOf(_sortHandle, pTarget, target.Length, pSource, source.Length, options, matchLengthPtr);
+ }
+ }
+ }
+
+ private unsafe int IndexOfOrdinalIgnoreCaseHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!target.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(target))
+ {
+ char* a = ap;
+ char* b = bp;
+ int endIndex = source.Length - target.Length;
+
+ if (endIndex < 0)
+ goto InteropCall;
+
+ for (int j = 0; j < target.Length; j++)
+ {
+ char targetChar = *(b + j);
+ if (targetChar >= 0x80 || s_highCharTable[targetChar])
+ goto InteropCall;
+ }
+
+ int i = 0;
+ for (; i <= endIndex; i++)
+ {
+ int targetIndex = 0;
+ int sourceIndex = i;
+
+ for (; targetIndex < target.Length; targetIndex++)
+ {
+ char valueChar = *(a + sourceIndex);
+ char targetChar = *(b + targetIndex);
+
+ if (valueChar == targetChar && valueChar < 0x80 && !s_highCharTable[valueChar])
+ {
+ sourceIndex++;
+ continue;
+ }
+
+ // uppercase both chars - notice that we need just one compare per char
+ if ((uint)(valueChar - 'a') <= ('z' - 'a'))
+ valueChar = (char)(valueChar - 0x20);
+ if ((uint)(targetChar - 'a') <= ('z' - 'a'))
+ targetChar = (char)(targetChar - 0x20);
+
+ if (valueChar >= 0x80 || s_highCharTable[valueChar])
+ goto InteropCall;
+ else if (valueChar != targetChar)
+ break;
+ sourceIndex++;
+ }
+
+ if (targetIndex == target.Length)
+ {
+ if (matchLengthPtr != null)
+ *matchLengthPtr = target.Length;
+ return i;
+ }
+ }
+ if (i > endIndex)
+ return -1;
+ InteropCall:
+ return Interop.Globalization.IndexOf(_sortHandle, b, target.Length, a, source.Length, options, matchLengthPtr);
+ }
+ }
+
+ private unsafe int IndexOfOrdinalHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!target.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(target))
+ {
+ char* a = ap;
+ char* b = bp;
+ int endIndex = source.Length - target.Length;
+
+ if (endIndex < 0)
+ goto InteropCall;
+
+ for (int j = 0; j < target.Length; j++)
+ {
+ char targetChar = *(b + j);
+ if (targetChar >= 0x80 || s_highCharTable[targetChar])
+ goto InteropCall;
+ }
+
+ int i = 0;
+ for (; i <= endIndex; i++)
+ {
+ int targetIndex = 0;
+ int sourceIndex = i;
+
+ for (; targetIndex < target.Length; targetIndex++)
+ {
+ char valueChar = *(a + sourceIndex);
+ char targetChar = *(b + targetIndex);
+ if (valueChar >= 0x80 || s_highCharTable[valueChar])
+ goto InteropCall;
+ else if (valueChar != targetChar)
+ break;
+ sourceIndex++;
+ }
+
+ if (targetIndex == target.Length)
+ {
+ if (matchLengthPtr != null)
+ *matchLengthPtr = target.Length;
+ return i;
+ }
+ }
+ if (i > endIndex)
+ return -1;
+ InteropCall:
+ return Interop.Globalization.IndexOf(_sortHandle, b, target.Length, a, source.Length, options, matchLengthPtr);
+ }
+ }
+
private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
{
Debug.Assert(!_invariantMode);
@@ -249,7 +443,7 @@ namespace System.Globalization
fixed (char* pSource = source)
{
- int lastIndex = Interop.GlobalizationInterop.LastIndexOf(_sortHandle, target, target.Length, pSource + (startIndex - count + 1), count, options);
+ int lastIndex = Interop.Globalization.LastIndexOf(_sortHandle, target, target.Length, pSource + (startIndex - count + 1), count, options);
return lastIndex != -1 ? lastIndex + leftStartIndex : -1;
}
@@ -270,7 +464,122 @@ namespace System.Globalization
}
#endif
- return Interop.GlobalizationInterop.StartsWith(_sortHandle, prefix, prefix.Length, source, source.Length, options);
+ return Interop.Globalization.StartsWith(_sortHandle, prefix, prefix.Length, source, source.Length, options);
+ }
+
+ private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!prefix.IsEmpty);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options))
+ {
+ if (source.Length < prefix.Length)
+ {
+ return false;
+ }
+
+ if ((options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase)
+ {
+ return StartsWithOrdinalIgnoreCaseHelper(source, prefix, options);
+ }
+ else
+ {
+ return StartsWithOrdinalHelper(source, prefix, options);
+ }
+ }
+ else
+ {
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pPrefix = &MemoryMarshal.GetReference(prefix))
+ {
+ return Interop.Globalization.StartsWith(_sortHandle, pPrefix, prefix.Length, pSource, source.Length, options);
+ }
+ }
+ }
+
+ private unsafe bool StartsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!prefix.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+ Debug.Assert(source.Length >= prefix.Length);
+
+ int length = prefix.Length;
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(prefix))
+ {
+ char* a = ap;
+ char* b = bp;
+
+ while (length != 0 && (*a < 0x80) && (*b < 0x80) && (!s_highCharTable[*a]) && (!s_highCharTable[*b]))
+ {
+ int charA = *a;
+ int charB = *b;
+
+ if (charA == charB)
+ {
+ a++; b++;
+ length--;
+ continue;
+ }
+
+ // uppercase both chars - notice that we need just one compare per char
+ if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
+ if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
+
+ if (charA != charB)
+ return false;
+
+ // Next char
+ a++; b++;
+ length--;
+ }
+
+ if (length == 0) return true;
+ return Interop.Globalization.StartsWith(_sortHandle, b, length, a, length, options);
+ }
+ }
+
+ private unsafe bool StartsWithOrdinalHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!prefix.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+ Debug.Assert(source.Length >= prefix.Length);
+
+ int length = prefix.Length;
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(prefix))
+ {
+ char* a = ap;
+ char* b = bp;
+
+ while (length != 0 && (*a < 0x80) && (*b < 0x80) && (!s_highCharTable[*a]) && (!s_highCharTable[*b]))
+ {
+ int charA = *a;
+ int charB = *b;
+
+ if (charA != charB)
+ return false;
+
+ // Next char
+ a++; b++;
+ length--;
+ }
+
+ if (length == 0) return true;
+ return Interop.Globalization.StartsWith(_sortHandle, b, length, a, length, options);
+ }
}
private bool EndsWith(string source, string suffix, CompareOptions options)
@@ -288,9 +597,124 @@ namespace System.Globalization
}
#endif
- return Interop.GlobalizationInterop.EndsWith(_sortHandle, suffix, suffix.Length, source, source.Length, options);
+ return Interop.Globalization.EndsWith(_sortHandle, suffix, suffix.Length, source, source.Length, options);
}
+ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!suffix.IsEmpty);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options))
+ {
+ if (source.Length < suffix.Length)
+ {
+ return false;
+ }
+
+ if ((options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase)
+ {
+ return EndsWithOrdinalIgnoreCaseHelper(source, suffix, options);
+ }
+ else
+ {
+ return EndsWithOrdinalHelper(source, suffix, options);
+ }
+ }
+ else
+ {
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pSuffix = &MemoryMarshal.GetReference(suffix))
+ {
+ return Interop.Globalization.EndsWith(_sortHandle, pSuffix, suffix.Length, pSource, source.Length, options);
+ }
+ }
+ }
+
+ private unsafe bool EndsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!suffix.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+ Debug.Assert(source.Length >= suffix.Length);
+
+ int length = suffix.Length;
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(suffix))
+ {
+ char* a = ap + source.Length - 1;
+ char* b = bp + suffix.Length - 1;
+
+ while (length != 0 && (*a < 0x80) && (*b < 0x80) && (!s_highCharTable[*a]) && (!s_highCharTable[*b]))
+ {
+ int charA = *a;
+ int charB = *b;
+
+ if (charA == charB)
+ {
+ a--; b--;
+ length--;
+ continue;
+ }
+
+ // uppercase both chars - notice that we need just one compare per char
+ if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
+ if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
+
+ if (charA != charB)
+ return false;
+
+ // Next char
+ a--; b--;
+ length--;
+ }
+
+ if (length == 0) return true;
+ return Interop.Globalization.EndsWith(_sortHandle, b - length + 1, length, a - length + 1, length, options);
+ }
+ }
+
+ private unsafe bool EndsWithOrdinalHelper(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!suffix.IsEmpty);
+ Debug.Assert(_isAsciiEqualityOrdinal);
+ Debug.Assert(source.Length >= suffix.Length);
+
+ int length = suffix.Length;
+
+ fixed (char* ap = &MemoryMarshal.GetReference(source))
+ fixed (char* bp = &MemoryMarshal.GetReference(suffix))
+ {
+ char* a = ap + source.Length - 1;
+ char* b = bp + suffix.Length - 1;
+
+ while (length != 0 && (*a < 0x80) && (*b < 0x80) && (!s_highCharTable[*a]) && (!s_highCharTable[*b]))
+ {
+ int charA = *a;
+ int charB = *b;
+
+ if (charA != charB)
+ return false;
+
+ // Next char
+ a--; b--;
+ length--;
+ }
+
+ if (length == 0) return true;
+ return Interop.Globalization.EndsWith(_sortHandle, b - length + 1, length, a - length + 1, length, options);
+ }
+ }
+
private unsafe SortKey CreateSortKey(String source, CompareOptions options)
{
Debug.Assert(!_invariantMode);
@@ -309,12 +733,12 @@ namespace System.Globalization
}
else
{
- int sortKeyLength = Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, null, 0, options);
+ int sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, source, source.Length, null, 0, options);
keyData = new byte[sortKeyLength];
fixed (byte* pSortKey = keyData)
{
- Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
+ Interop.Globalization.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
}
}
@@ -335,7 +759,7 @@ namespace System.Globalization
if (index == length - 1 || !Char.IsLowSurrogate(text[index+1]))
return false; // unpaired surrogate
- uc = CharUnicodeInfo.InternalGetUnicodeCategory(Char.ConvertToUtf32(text[index], text[index+1]));
+ uc = CharUnicodeInfo.GetUnicodeCategory(Char.ConvertToUtf32(text[index], text[index+1]));
if (uc == UnicodeCategory.PrivateUse || uc == UnicodeCategory.OtherNotAssigned)
return false;
@@ -376,13 +800,13 @@ namespace System.Globalization
return 0;
}
- int sortKeyLength = Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, null, 0, options);
+ int sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, source, source.Length, null, 0, options);
// As an optimization, for small sort keys we allocate the buffer on the stack.
if (sortKeyLength <= 256)
{
byte* pSortKey = stackalloc byte[sortKeyLength];
- Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
+ Interop.Globalization.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
return InternalHashSortKey(pSortKey, sortKeyLength);
}
@@ -390,7 +814,7 @@ namespace System.Globalization
fixed (byte* pSortKey = &sortKey[0])
{
- Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
+ Interop.Globalization.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
return InternalHashSortKey(pSortKey, sortKeyLength);
}
}
@@ -455,12 +879,145 @@ namespace System.Globalization
{
Debug.Assert(!_invariantMode);
- int sortVersion = Interop.GlobalizationInterop.GetSortVersion(_sortHandle);
+ int sortVersion = Interop.Globalization.GetSortVersion(_sortHandle);
return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0,
(byte) (LCID >> 24),
(byte) ((LCID & 0x00FF0000) >> 16),
(byte) ((LCID & 0x0000FF00) >> 8),
(byte) (LCID & 0xFF)));
}
+
+ // See https://github.com/dotnet/coreclr/blob/master/src/utilcode/util_nodependencies.cpp#L970
+ private static readonly bool[] s_highCharTable = new bool[0x80]
+ {
+ true, /* 0x0, 0x0 */
+ true, /* 0x1, .*/
+ true, /* 0x2, .*/
+ true, /* 0x3, .*/
+ true, /* 0x4, .*/
+ true, /* 0x5, .*/
+ true, /* 0x6, .*/
+ true, /* 0x7, .*/
+ true, /* 0x8, .*/
+ false, /* 0x9, */
+ true, /* 0xA, */
+ false, /* 0xB, .*/
+ false, /* 0xC, .*/
+ true, /* 0xD, */
+ true, /* 0xE, .*/
+ true, /* 0xF, .*/
+ true, /* 0x10, .*/
+ true, /* 0x11, .*/
+ true, /* 0x12, .*/
+ true, /* 0x13, .*/
+ true, /* 0x14, .*/
+ true, /* 0x15, .*/
+ true, /* 0x16, .*/
+ true, /* 0x17, .*/
+ true, /* 0x18, .*/
+ true, /* 0x19, .*/
+ true, /* 0x1A, */
+ true, /* 0x1B, .*/
+ true, /* 0x1C, .*/
+ true, /* 0x1D, .*/
+ true, /* 0x1E, .*/
+ true, /* 0x1F, .*/
+ false, /*0x20, */
+ false, /*0x21, !*/
+ false, /*0x22, "*/
+ false, /*0x23, #*/
+ false, /*0x24, $*/
+ false, /*0x25, %*/
+ false, /*0x26, &*/
+ true, /*0x27, '*/
+ false, /*0x28, (*/
+ false, /*0x29, )*/
+ false, /*0x2A **/
+ false, /*0x2B, +*/
+ false, /*0x2C, ,*/
+ true, /*0x2D, -*/
+ false, /*0x2E, .*/
+ false, /*0x2F, /*/
+ false, /*0x30, 0*/
+ false, /*0x31, 1*/
+ false, /*0x32, 2*/
+ false, /*0x33, 3*/
+ false, /*0x34, 4*/
+ false, /*0x35, 5*/
+ false, /*0x36, 6*/
+ false, /*0x37, 7*/
+ false, /*0x38, 8*/
+ false, /*0x39, 9*/
+ false, /*0x3A, :*/
+ false, /*0x3B, ;*/
+ false, /*0x3C, <*/
+ false, /*0x3D, =*/
+ false, /*0x3E, >*/
+ false, /*0x3F, ?*/
+ false, /*0x40, @*/
+ false, /*0x41, A*/
+ false, /*0x42, B*/
+ false, /*0x43, C*/
+ false, /*0x44, D*/
+ false, /*0x45, E*/
+ false, /*0x46, F*/
+ false, /*0x47, G*/
+ false, /*0x48, H*/
+ false, /*0x49, I*/
+ false, /*0x4A, J*/
+ false, /*0x4B, K*/
+ false, /*0x4C, L*/
+ false, /*0x4D, M*/
+ false, /*0x4E, N*/
+ false, /*0x4F, O*/
+ false, /*0x50, P*/
+ false, /*0x51, Q*/
+ false, /*0x52, R*/
+ false, /*0x53, S*/
+ false, /*0x54, T*/
+ false, /*0x55, U*/
+ false, /*0x56, V*/
+ false, /*0x57, W*/
+ false, /*0x58, X*/
+ false, /*0x59, Y*/
+ false, /*0x5A, Z*/
+ false, /*0x5B, [*/
+ false, /*0x5C, \*/
+ false, /*0x5D, ]*/
+ false, /*0x5E, ^*/
+ false, /*0x5F, _*/
+ false, /*0x60, `*/
+ false, /*0x61, a*/
+ false, /*0x62, b*/
+ false, /*0x63, c*/
+ false, /*0x64, d*/
+ false, /*0x65, e*/
+ false, /*0x66, f*/
+ false, /*0x67, g*/
+ false, /*0x68, h*/
+ false, /*0x69, i*/
+ false, /*0x6A, j*/
+ false, /*0x6B, k*/
+ false, /*0x6C, l*/
+ false, /*0x6D, m*/
+ false, /*0x6E, n*/
+ false, /*0x6F, o*/
+ false, /*0x70, p*/
+ false, /*0x71, q*/
+ false, /*0x72, r*/
+ false, /*0x73, s*/
+ false, /*0x74, t*/
+ false, /*0x75, u*/
+ false, /*0x76, v*/
+ false, /*0x77, w*/
+ false, /*0x78, x*/
+ false, /*0x79, y*/
+ false, /*0x7A, z*/
+ false, /*0x7B, {*/
+ false, /*0x7C, |*/
+ false, /*0x7D, }*/
+ false, /*0x7E, ~*/
+ true, /*0x7F, */
+ };
}
}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs
index 958dc727c..c0befb263 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs
@@ -51,6 +51,28 @@ namespace System.Globalization
return ret < 0 ? ret : ret + offset;
}
}
+
+ private static unsafe int FindStringOrdinal(
+ uint dwFindStringOrdinalFlags,
+ ReadOnlySpan<char> source,
+ ReadOnlySpan<char> value,
+ bool bIgnoreCase)
+ {
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pValue = &MemoryMarshal.GetReference(value))
+ {
+ int ret = Interop.Kernel32.FindStringOrdinal(
+ dwFindStringOrdinalFlags,
+ pSource,
+ source.Length,
+ pValue,
+ value.Length,
+ bIgnoreCase ? 1 : 0);
+ return ret;
+ }
+ }
internal static int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
{
@@ -62,6 +84,16 @@ namespace System.Globalization
return FindStringOrdinal(FIND_FROMSTART, source, startIndex, count, value, value.Length, ignoreCase);
}
+ internal static int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ Debug.Assert(source.Length != 0);
+ Debug.Assert(value.Length != 0);
+
+ return FindStringOrdinal(FIND_FROMSTART, source, value, ignoreCase);
+ }
+
internal static int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
{
Debug.Assert(!GlobalizationMode.Invariant);
@@ -178,6 +210,34 @@ namespace System.Globalization
}
private unsafe int FindString(
+ uint dwFindNLSStringFlags,
+ ReadOnlySpan<char> lpStringSource,
+ ReadOnlySpan<char> lpStringValue,
+ int* pcchFound)
+ {
+ Debug.Assert(!_invariantMode);
+
+ string localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
+
+ fixed (char* pLocaleName = localeName)
+ fixed (char* pSource = &MemoryMarshal.GetReference(lpStringSource))
+ fixed (char* pValue = &MemoryMarshal.GetReference(lpStringValue))
+ {
+ return Interop.Kernel32.FindNLSStringEx(
+ pLocaleName,
+ dwFindNLSStringFlags,
+ pSource,
+ lpStringSource.Length,
+ pValue,
+ lpStringValue.Length,
+ pcchFound,
+ null,
+ null,
+ _sortHandle);
+ }
+ }
+
+ private unsafe int FindString(
uint dwFindNLSStringFlags,
string lpStringSource,
int startSource,
@@ -256,6 +316,18 @@ namespace System.Globalization
return -1;
}
+ internal unsafe int IndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(source.Length != 0);
+ Debug.Assert(target.Length != 0);
+ Debug.Assert((options == CompareOptions.None || options == CompareOptions.IgnoreCase));
+
+ int retValue = FindString(FIND_FROMSTART | (uint)GetNativeCompareFlags(options), source, target, matchLengthPtr);
+ return retValue;
+ }
+
private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
{
Debug.Assert(!_invariantMode);
@@ -311,6 +383,17 @@ namespace System.Globalization
null) >= 0;
}
+ private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!prefix.IsEmpty);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return FindString(FIND_STARTSWITH | (uint)GetNativeCompareFlags(options), source, prefix, null) >= 0;
+ }
+
private unsafe bool EndsWith(string source, string suffix, CompareOptions options)
{
Debug.Assert(!_invariantMode);
@@ -329,6 +412,17 @@ namespace System.Globalization
null) >= 0;
}
+ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+ {
+ Debug.Assert(!_invariantMode);
+
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!suffix.IsEmpty);
+ Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return FindString(FIND_ENDSWITH | (uint)GetNativeCompareFlags(options), source, suffix, null) >= 0;
+ }
+
// PAL ends here
[NonSerialized]
private IntPtr _sortHandle;
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
index 5a06bccd4..c50831b09 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
@@ -30,5 +30,10 @@ namespace System.Globalization
return cultureInfo;
}
+
+ private static CultureInfo GetUserDefaultUICulture()
+ {
+ return s_userDefaultCulture ?? InitializeUserDefaultCulture();
+ }
}
}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
index f5a6992e6..df13759f7 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
@@ -56,5 +56,36 @@ namespace System.Globalization
return temp;
}
+
+ private static CultureInfo GetUserDefaultUICulture()
+ {
+#if !ENABLE_WINRT
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
+ const uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention
+ uint langCount = 0;
+ uint bufLen = 0;
+
+ if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, out langCount, null, ref bufLen))
+ {
+ char[] languages = new char[bufLen];
+ if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, out langCount, languages, ref bufLen))
+ {
+ int index = 0;
+ while (languages[index] != (char)0 && index < languages.Length)
+ {
+ index++;
+ }
+
+ CultureInfo temp = GetCultureByName(new String(languages, 0, index), true);
+ temp._isReadOnly = true;
+ return temp;
+ }
+ }
+#endif
+
+ return s_userDefaultCulture ?? InitializeUserDefaultCulture();
+ }
}
}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs
index bc4411ab7..08be455f9 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs
@@ -80,7 +80,7 @@ namespace System.Globalization
// textinfo and compareinfo names are the same as the name
// Note that the name used to be serialized for Everett; it is now serialized
- // because alernate sorts can have alternate names.
+ // because alternate sorts can have alternate names.
// This has a de-DE, de-DE_phoneb or fj-FJ style name
internal string _name;
@@ -100,12 +100,8 @@ namespace System.Globalization
//
//--------------------------------------------------------------------//
- //Get the current user default culture. This one is almost always used, so we create it by default.
private static volatile CultureInfo s_userDefaultCulture;
-
- //
- // All of the following will be created on demand.
- //
+ private static volatile CultureInfo s_userDefaultUICulture;
// WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture)
// See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
@@ -113,7 +109,7 @@ namespace System.Globalization
// Get in touch with the diagnostics team if you have questions.
//The Invariant culture;
- private static volatile CultureInfo s_InvariantCultureInfo;
+ private static readonly CultureInfo s_InvariantCultureInfo = new CultureInfo(CultureData.Invariant, isReadOnly: true);
//These are defaults that we use if a thread has not opted into having an explicit culture
private static volatile CultureInfo s_DefaultThreadCurrentUICulture;
@@ -140,23 +136,16 @@ namespace System.Globalization
internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00;
internal const int LOCALE_INVARIANT = 0x007F;
- //
- // The CultureData instance that reads the data provided by our CultureData class.
- //
- // Using a field initializer rather than a static constructor so that the whole class can be lazy
- // init.
- private static readonly bool s_init = Init();
- private static bool Init()
+ private static CultureInfo InitializeUserDefaultCulture()
{
- if (s_InvariantCultureInfo == null)
- {
- CultureInfo temp = new CultureInfo("", false);
- temp._isReadOnly = true;
- s_InvariantCultureInfo = temp;
- }
+ Interlocked.CompareExchange(ref s_userDefaultCulture, GetUserDefaultCulture(), null);
+ return s_userDefaultCulture;
+ }
- s_userDefaultCulture = GetUserDefaultCulture();
- return true;
+ private static CultureInfo InitializeUserDefaultUICulture()
+ {
+ Interlocked.CompareExchange(ref s_userDefaultUICulture, GetUserDefaultUICulture(), null);
+ return s_userDefaultUICulture;
}
////////////////////////////////////////////////////////////////////////
@@ -180,22 +169,23 @@ namespace System.Globalization
}
// Get our data providing record
- this._cultureData = CultureData.GetCultureData(name, useUserOverride);
+ _cultureData = CultureData.GetCultureData(name, useUserOverride);
- if (this._cultureData == null)
+ if (_cultureData == null)
throw new CultureNotFoundException(
nameof(name), name, SR.Argument_CultureNotSupported);
- this._name = this._cultureData.CultureName;
- this._isInherited = !this.EETypePtr.FastEquals(EETypePtr.EETypePtrOf<CultureInfo>());
+ _name = _cultureData.CultureName;
+ _isInherited = !this.EETypePtr.FastEquals(EETypePtr.EETypePtrOf<CultureInfo>());
}
- private CultureInfo(CultureData cultureData)
+ private CultureInfo(CultureData cultureData, bool isReadOnly = false)
{
Debug.Assert(cultureData != null);
_cultureData = cultureData;
_name = cultureData.CultureName;
_isInherited = false;
+ _isReadOnly = isReadOnly;
}
private static CultureInfo CreateCultureInfoNoThrow(string name, bool useUserOverride)
@@ -222,11 +212,6 @@ namespace System.Globalization
throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
}
- InitializeFromCultureId(culture, useUserOverride);
- }
-
- private void InitializeFromCultureId(int culture, bool useUserOverride)
- {
switch (culture)
{
case LOCALE_CUSTOM_DEFAULT:
@@ -354,15 +339,6 @@ namespace System.Globalization
return (new CultureInfo(culture._cultureData.SSPECIFICCULTURE));
}
- // //
- // // Return a specific culture. A tad irrelevent now since we always return valid data
- // // for neutral locales.
- // //
- // // Note that there's interesting behavior that tries to find a smaller name, ala RFC4647,
- // // if we can't find a bigger name. That doesn't help with things like "zh" though, so
- // // the approach is of questionable value
- // //
-
internal static bool VerifyCultureName(String cultureName, bool throwException)
{
// This function is used by ResourceManager.GetResourceFileName().
@@ -439,15 +415,7 @@ namespace System.Globalization
return ci;
}
- // if s_userDefaultCulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
- // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn't get chance to initialize
- if (s_userDefaultCulture == null)
- {
- Init();
- }
-
- Debug.Assert(s_userDefaultCulture != null);
- return s_userDefaultCulture;
+ return s_userDefaultCulture ?? InitializeUserDefaultCulture();
}
set
@@ -491,15 +459,7 @@ namespace System.Globalization
return ci;
}
- // if s_userDefaultCulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
- // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn't get chance to initialize
- if (s_userDefaultCulture == null)
- {
- Init();
- }
-
- Debug.Assert(s_userDefaultCulture != null);
- return s_userDefaultCulture;
+ return s_userDefaultUICulture ?? InitializeUserDefaultUICulture();
}
set
@@ -530,18 +490,9 @@ namespace System.Globalization
s_currentThreadUICulture = null;
}
- public static CultureInfo InstalledUICulture
- {
- get
- {
- if (s_userDefaultCulture == null)
- {
- Init();
- }
- Debug.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null");
- return s_userDefaultCulture;
- }
- }
+ internal static CultureInfo UserDefaultUICulture => s_userDefaultUICulture ?? InitializeUserDefaultUICulture();
+
+ public static CultureInfo InstalledUICulture => s_userDefaultCulture ?? InitializeUserDefaultCulture();
public static CultureInfo DefaultThreadCurrentCulture
{
@@ -593,6 +544,7 @@ namespace System.Globalization
{
get
{
+ Debug.Assert(s_InvariantCultureInfo != null);
return (s_InvariantCultureInfo);
}
}
@@ -823,7 +775,7 @@ namespace System.Globalization
// Since CompareInfo's don't have any overrideable properties, get the CompareInfo from
// the Non-Overridden CultureInfo so that we only create one CompareInfo per culture
CompareInfo temp = UseUserOverride
- ? GetCultureInfo(this._name).CompareInfo
+ ? GetCultureInfo(_name).CompareInfo
: new CompareInfo(this);
if (OkayToCacheClassWithCompatibilityBehavior)
{
@@ -860,7 +812,7 @@ namespace System.Globalization
if (_textInfo == null)
{
// Make a new textInfo
- TextInfo tempTextInfo = new TextInfo(this._cultureData);
+ TextInfo tempTextInfo = new TextInfo(_cultureData);
tempTextInfo.SetReadOnlyState(_isReadOnly);
if (OkayToCacheClassWithCompatibilityBehavior)
@@ -950,7 +902,7 @@ namespace System.Globalization
{
get
{
- return this._cultureData.IsNeutralCulture;
+ return _cultureData.IsNeutralCulture;
}
}
@@ -985,7 +937,7 @@ namespace System.Globalization
{
if (numInfo == null)
{
- NumberFormatInfo temp = new NumberFormatInfo(this._cultureData);
+ NumberFormatInfo temp = new NumberFormatInfo(_cultureData);
temp.isReadOnly = _isReadOnly;
Interlocked.CompareExchange(ref numInfo, temp, null);
}
@@ -1017,7 +969,7 @@ namespace System.Globalization
if (dateTimeInfo == null)
{
// Change the calendar of DTFI to the specified calendar of this CultureInfo.
- DateTimeFormatInfo temp = new DateTimeFormatInfo(this._cultureData, this.Calendar);
+ DateTimeFormatInfo temp = new DateTimeFormatInfo(_cultureData, this.Calendar);
temp._isReadOnly = _isReadOnly;
Interlocked.CompareExchange(ref dateTimeInfo, temp, null);
}
@@ -1037,7 +989,9 @@ namespace System.Globalization
public void ClearCachedData()
{
- s_userDefaultCulture = null;
+ // reset the default culture values
+ s_userDefaultCulture = GetUserDefaultCulture();
+ s_userDefaultUICulture = GetUserDefaultUICulture();
RegionInfo.s_currentRegionInfo = null;
#pragma warning disable 0618 // disable the obsolete warning
@@ -1115,10 +1069,10 @@ namespace System.Globalization
{
if (_calendar == null)
{
- Debug.Assert(this._cultureData.CalendarIds.Length > 0, "this._cultureData.CalendarIds.Length > 0");
+ Debug.Assert(_cultureData.CalendarIds.Length > 0, "_cultureData.CalendarIds.Length > 0");
// Get the default calendar for this culture. Note that the value can be
// from registry if this is a user default culture.
- Calendar newObj = this._cultureData.DefaultCalendar;
+ Calendar newObj = _cultureData.DefaultCalendar;
System.Threading.Interlocked.MemoryBarrier();
newObj.SetReadOnlyState(_isReadOnly);
@@ -1143,7 +1097,7 @@ namespace System.Globalization
//
// This property always returns a new copy of the calendar array.
//
- CalendarId[] calID = this._cultureData.CalendarIds;
+ CalendarId[] calID = _cultureData.CalendarIds;
Calendar[] cals = new Calendar[calID.Length];
for (int i = 0; i < cals.Length; i++)
{
diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
index e4ab832d5..f292d54dc 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
@@ -13,7 +13,7 @@ namespace System.Globalization
bool invariantEnabled = false;
if (!invariantEnabled)
{
- if (Interop.GlobalizationInterop.LoadICU() == 0)
+ if (Interop.Globalization.LoadICU() == 0)
{
string message = "Couldn't find a valid ICU package installed on the system. " +
"Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.";
diff --git a/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs b/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs
deleted file mode 100644
index 59dffd7d9..000000000
--- a/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Win32;
-using Microsoft.Win32.SafeHandles;
-using System.Runtime.InteropServices;
-
-namespace System.IO
-{
- internal static partial class InternalFile
- {
- internal static bool InternalExists(String path)
- {
- Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA();
- int errorCode = FillAttributeInfo(path, ref data, false, true);
-
- return (errorCode == 0) && (data.fileAttributes != -1)
- && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0);
- }
-
- // Returns 0 on success, otherwise a Win32 error code. Note that
- // classes should use -1 as the uninitialized state for dataInitialized.
- internal static int FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound)
- {
- int errorCode = 0;
- if (tryagain) // someone has a handle to the file open, or other error
- {
- Interop.Kernel32.WIN32_FIND_DATA findData;
- findData = new Interop.Kernel32.WIN32_FIND_DATA();
-
- // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error
- String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
-
- // For floppy drives, normally the OS will pop up a dialog saying
- // there is no disk in drive A:, please insert one. We don't want that.
- // SetThreadErrorMode will let us disable this, but we should set the error
- // mode back, since this may have wide-ranging effects.
- uint oldMode;
- bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
- try
- {
- bool error = false;
- SafeFindHandle handle = Interop.Kernel32.FindFirstFile(tempPath, ref findData);
- try
- {
- if (handle.IsInvalid)
- {
- error = true;
- errorCode = Marshal.GetLastWin32Error();
-
- if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND ||
- errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND ||
- errorCode == Interop.Errors.ERROR_NOT_READY) // floppy device not ready
- {
- if (!returnErrorOnNotFound)
- {
- // Return default value for backward compatibility
- errorCode = 0;
- data.fileAttributes = -1;
- }
- }
- return errorCode;
- }
- }
- finally
- {
- // Close the Win32 handle
- try
- {
- handle.Dispose();
- }
- catch
- {
- // if we're already returning an error, don't throw another one.
- if (!error)
- {
- throw Win32Marshal.GetExceptionForLastWin32Error();
- }
- }
- }
- }
- finally
- {
- if (setThreadErrorModeSuccess)
- Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode);
- }
-
- // Copy the information to data
- data.PopulateFrom(ref findData);
- }
- else
- {
- // For floppy drives, normally the OS will pop up a dialog saying
- // there is no disk in drive A:, please insert one. We don't want that.
- // SetThreadErrorMode will let us disable this, but we should set the error
- // mode back, since this may have wide-ranging effects.
- bool success = false;
- uint oldMode;
- bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
- try
- {
- success = Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data);
- }
- finally
- {
- if (setThreadErrorModeSuccess)
- Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode);
- }
-
- if (!success)
- {
- errorCode = Marshal.GetLastWin32Error();
- if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND &&
- errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND &&
- errorCode != Interop.Errors.ERROR_NOT_READY) // floppy device not ready
- {
- // In case someone latched onto the file. Take the perf hit only for failure
- return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound);
- }
- else
- {
- if (!returnErrorOnNotFound)
- {
- // Return default value for backward compatibility
- errorCode = 0;
- data.fileAttributes = -1;
- }
- }
- }
- }
-
- return errorCode;
- }
- }
-}
diff --git a/src/System.Private.CoreLib/src/System/IO/Stream.cs b/src/System.Private.CoreLib/src/System/IO/Stream.cs
index b4f8ab30d..c307ec91e 100644
--- a/src/System.Private.CoreLib/src/System/IO/Stream.cs
+++ b/src/System.Private.CoreLib/src/System/IO/Stream.cs
@@ -242,7 +242,7 @@ namespace System.IO
public virtual ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
{
- if (destination.TryGetArray(out ArraySegment<byte> array))
+ if (MemoryMarshal.TryGetArray(destination, out ArraySegment<byte> array))
{
return new ValueTask<int>(ReadAsync(array.Array, array.Offset, array.Count, cancellationToken));
}
@@ -316,17 +316,17 @@ namespace System.IO
buffer, offset, count, this);
}
- public virtual Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ public virtual ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
{
if (MemoryMarshal.TryGetArray(source, out ArraySegment<byte> array))
{
- return WriteAsync(array.Array, array.Offset, array.Count, cancellationToken);
+ return new ValueTask(WriteAsync(array.Array, array.Offset, array.Count, cancellationToken));
}
else
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
source.Span.CopyTo(buffer);
- return FinishWriteAsync(WriteAsync(buffer, 0, source.Length, cancellationToken), buffer);
+ return new ValueTask(FinishWriteAsync(WriteAsync(buffer, 0, source.Length, cancellationToken), buffer));
async Task FinishWriteAsync(Task writeTask, byte[] localBuffer)
{
@@ -559,7 +559,7 @@ namespace System.IO
cancellationToken.ThrowIfCancellationRequested();
}
- public override async Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
+ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
}
diff --git a/src/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/System.Private.CoreLib/src/System/InvokeUtils.cs
index 8b92cfef3..69435aee9 100644
--- a/src/System.Private.CoreLib/src/System/InvokeUtils.cs
+++ b/src/System.Private.CoreLib/src/System/InvokeUtils.cs
@@ -14,6 +14,7 @@ using Internal.Runtime.CompilerServices;
namespace System
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
[System.Runtime.CompilerServices.DependencyReductionRoot]
public static class InvokeUtils
{
@@ -32,7 +33,7 @@ namespace System
//
// null converted to default(T) (this is important when T is a valuetype.)
//
- // There is also another transform of T -> Nullable<T>. This method acknowleges that rule but does not actually transform the T.
+ // There is also another transform of T -> Nullable<T>. This method acknowledges that rule but does not actually transform the T.
// Rather, the transformation happens naturally when the caller unboxes the value to its final destination.
//
// This method is targeted by the Delegate ILTransformer.
diff --git a/src/System.Private.CoreLib/src/System/Math.CoreRT.cs b/src/System.Private.CoreLib/src/System/Math.CoreRT.cs
index dc4e40a17..1f5edbb98 100644
--- a/src/System.Private.CoreLib/src/System/Math.CoreRT.cs
+++ b/src/System.Private.CoreLib/src/System/Math.CoreRT.cs
@@ -20,7 +20,7 @@ namespace System
[Intrinsic]
public static float Abs(float value)
{
- return (float)RuntimeImports.fabs(value);
+ return RuntimeImports.fabsf(value);
}
[Intrinsic]
@@ -36,12 +36,24 @@ namespace System
}
[Intrinsic]
+ public static double Acosh(double d)
+ {
+ return RuntimeImports.acosh(d);
+ }
+
+ [Intrinsic]
public static double Asin(double d)
{
return RuntimeImports.asin(d);
}
[Intrinsic]
+ public static double Asinh(double d)
+ {
+ return RuntimeImports.asinh(d);
+ }
+
+ [Intrinsic]
public static double Atan(double d)
{
return RuntimeImports.atan(d);
@@ -49,11 +61,23 @@ namespace System
[Intrinsic]
public static double Atan2(double y, double x)
- {
+ {
return RuntimeImports.atan2(y, x);
}
[Intrinsic]
+ public static double Atanh(double d)
+ {
+ return RuntimeImports.atanh(d);
+ }
+
+ [Intrinsic]
+ public static double Cbrt(double d)
+ {
+ return RuntimeImports.cbrt(d);
+ }
+
+ [Intrinsic]
public static double Ceiling(double a)
{
return RuntimeImports.ceil(a);
@@ -73,7 +97,7 @@ namespace System
[Intrinsic]
public static double Exp(double d)
- {
+ {
return RuntimeImports.exp(d);
}
diff --git a/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs b/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs
index b67b8bbd4..be61cf73c 100644
--- a/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs
+++ b/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs
@@ -17,123 +17,142 @@ namespace System
{
public static partial class MathF
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Acos(float x)
{
- return (float)Math.Acos(x);
+ return RuntimeImports.acosf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
+ public static float Acosh(float x)
+ {
+ return RuntimeImports.acoshf(x);
+ }
+
+ [Intrinsic]
public static float Asin(float x)
{
- return (float)Math.Asin(x);
+ return RuntimeImports.asinf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
+ public static float Asinh(float x)
+ {
+ return RuntimeImports.asinhf(x);
+ }
+
+ [Intrinsic]
public static float Atan(float x)
{
- return (float)Math.Atan(x);
+ return RuntimeImports.atanf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Atan2(float y, float x)
{
- return (float)Math.Atan2(y, x);
+ return RuntimeImports.atan2f(y, x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
+ public static float Atanh(float x)
+ {
+ return RuntimeImports.atanhf(x);
+ }
+
+ [Intrinsic]
+ public static float Cbrt(float x)
+ {
+ return RuntimeImports.cbrtf(x);
+ }
+
+ [Intrinsic]
public static float Ceiling(float x)
{
- return (float)Math.Ceiling(x);
+ return RuntimeImports.ceilf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Cos(float x)
{
- return (float)Math.Cos(x);
+ return RuntimeImports.cosf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Cosh(float x)
{
- return (float)Math.Cosh(x);
+ return RuntimeImports.coshf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Exp(float x)
{
- return (float)Math.Exp(x);
+ return RuntimeImports.expf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Floor(float x)
{
- return (float)Math.Floor(x);
+ return RuntimeImports.floorf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Log(float x)
{
- return (float)Math.Log(x);
+ return RuntimeImports.logf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Log10(float x)
{
- return (float)Math.Log10(x);
+ return RuntimeImports.log10f(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Pow(float x, float y)
{
- return (float)Math.Pow(x, y);
+ return RuntimeImports.powf(x, y);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Sin(float x)
{
- return (float)Math.Sin(x);
+ return RuntimeImports.sinf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Sinh(float x)
{
- return (float)Math.Sinh(x);
+ return RuntimeImports.sinhf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Sqrt(float x)
{
- return (float)Math.Sqrt(x);
+ return RuntimeImports.sqrtf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Tan(float x)
{
- return (float)Math.Tan(x);
+ return RuntimeImports.tanf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
public static float Tanh(float x)
{
- return (float)Math.Tanh(x);
+ return RuntimeImports.tanhf(x);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
private static float FMod(float x, float y)
{
- return (float)RuntimeImports.fmod(x, y);
+ return RuntimeImports.fmodf(x, y);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Intrinsic]
private static unsafe float ModF(float x, float* intptr)
{
- //todo : https://github.com/dotnet/corert/issues/3167
- double d = x;
- double r = RuntimeImports.modf(d, &d);
-
- *intptr = (float)d;
- return (float)r;
+ return RuntimeImports.modff(x, intptr);
}
}
}
diff --git a/src/System.Private.CoreLib/src/System/MissingFieldException.cs b/src/System.Private.CoreLib/src/System/MissingFieldException.cs
index 95d517cca..88cd41c6a 100644
--- a/src/System.Private.CoreLib/src/System/MissingFieldException.cs
+++ b/src/System.Private.CoreLib/src/System/MissingFieldException.cs
@@ -35,10 +35,10 @@ namespace System
HResult = HResults.COR_E_MISSINGFIELD;
}
- public MissingFieldException(string className, string methodName)
+ public MissingFieldException(string className, string fieldName)
{
ClassName = className;
- MemberName = methodName;
+ MemberName = fieldName;
}
protected MissingFieldException(SerializationInfo info, StreamingContext context)
diff --git a/src/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/System.Private.CoreLib/src/System/MulticastDelegate.cs
index 7368f2a15..a3a23e67a 100644
--- a/src/System.Private.CoreLib/src/System/MulticastDelegate.cs
+++ b/src/System.Private.CoreLib/src/System/MulticastDelegate.cs
@@ -118,18 +118,22 @@ namespace System
public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2)
{
- if ((Object)d1 == null)
- return (Object)d2 == null;
+ if (ReferenceEquals(d1, d2))
+ {
+ return true;
+ }
- return d1.Equals(d2);
+ return d1 is null ? false : d1.Equals(d2);
}
public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2)
{
- if ((Object)d1 == null)
- return (Object)d2 != null;
+ if (ReferenceEquals(d1, d2))
+ {
+ return false;
+ }
- return !d1.Equals(d2);
+ return d1 is null ? true : !d1.Equals(d2);
}
public override sealed Delegate[] GetInvocationList()
diff --git a/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs b/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs
index 3aa1541a8..dab7c131a 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs
@@ -10,6 +10,7 @@ using System.Collections.Generic;
namespace System.Reflection
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public static partial class AssemblyNameHelpers
{
//
diff --git a/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs b/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs
index a29919faa..f963e0323 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs
@@ -15,6 +15,7 @@ namespace System.Reflection
//
// Parses an assembly name.
//
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public static class AssemblyNameParser
{
public static void Parse(AssemblyName blank, String s)
diff --git a/src/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs b/src/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs
index 76dea8d98..4399ee559 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs
@@ -12,6 +12,7 @@ namespace System.Reflection
// to manage.)
//
// This is not an api type but needs to be public as both Reflection.Core and System.Private.Corelib accesses it.
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public sealed class BinderBundle
{
public BinderBundle(Binder binder, CultureInfo culture)
diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttributeExtensions.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttributeExtensions.cs
index 55a306fa6..f2143f8e0 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttributeExtensions.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttributeExtensions.cs
@@ -345,7 +345,18 @@ namespace System.Reflection
}
int count = attributes.Count;
Attribute[] result;
- result = (Attribute[])Array.CreateInstance(actualElementType, count);
+ try
+ {
+ result = (Attribute[])Array.CreateInstance(actualElementType, count);
+ }
+ catch (NotSupportedException) when (actualElementType.ContainsGenericParameters)
+ {
+ // This is here for desktop compatibility (using try-catch as control flow to avoid slowing down the mainline case.)
+ // CustomAttributeExtensions.GetCustomAttributes() normally returns an array of the exact attribute type requested except when
+ // the reqested type is an open type. Its ICustomAttributeProvider counterpart would return an Object[] array but that's
+ // not possible with this api's return type so it returns null instead.
+ return null;
+ }
attributes.CopyTo(result, 0);
return result;
}
diff --git a/src/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeImplementedCustomAttributeData.cs b/src/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeImplementedCustomAttributeData.cs
index 5e75540e7..f21515b1e 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeImplementedCustomAttributeData.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeImplementedCustomAttributeData.cs
@@ -12,6 +12,7 @@ namespace System.Reflection.Runtime.CustomAttributes
// If a CustomAttributeData implementation derives from this, it is a hint that it has a AttributeType implementation
// that's more efficient than building a ConstructorInfo and gettings its DeclaredType.
//
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class RuntimeImplementedCustomAttributeData : CustomAttributeData
{
public new abstract Type AttributeType { get; }
diff --git a/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs b/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs
index 54240cb5f..174a41975 100644
--- a/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs
+++ b/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs
@@ -4,9 +4,6 @@
/*============================================================
**
-**
-**
-**
**
** Purpose: Searches for resources on disk, used for file-
** based resource lookup.
@@ -14,19 +11,16 @@
**
===========================================================*/
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Threading;
+
+using Internal.IO;
+
namespace System.Resources
{
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Globalization;
- using System.Runtime.CompilerServices;
- using System.Runtime.Versioning;
- using System.Text;
- using System.Threading;
- using System.Diagnostics;
-
internal class FileBasedResourceGroveler : IResourceGroveler
{
private ResourceManager.ResourceManagerMediator _mediator;
@@ -89,14 +83,14 @@ namespace System.Resources
if (_mediator.ModuleDir != null)
{
String path = Path.Combine(_mediator.ModuleDir, fileName);
- if (InternalFile.Exists(path))
+ if (File.Exists(path))
{
return path;
}
}
// look in .
- if (InternalFile.Exists(fileName))
+ if (File.Exists(fileName))
return fileName;
return null; // give up.
diff --git a/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs b/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs
index 4fdec754f..fc2241351 100644
--- a/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs
+++ b/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs
@@ -357,11 +357,10 @@ namespace System.Resources
sb.Append(name);
String givenName = sb.ToString();
- CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo;
String canonicalName = null;
foreach (String existingName in satellite.GetManifestResourceNames())
{
- if (comparer.Compare(existingName, givenName, CompareOptions.IgnoreCase) == 0)
+ if (String.Equals(existingName, givenName, StringComparison.InvariantCultureIgnoreCase))
{
if (canonicalName == null)
{
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
index 3cabd25e6..5601bae5a 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
@@ -71,9 +71,7 @@ namespace System.Runtime.CompilerServices
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
- {
- AsyncMethodBuilderCore.Start(ref stateMachine);
- }
+ => AsyncMethodBuilderCore.Start(ref stateMachine);
/// <summary>Associates the builder with the state machine it represents.</summary>
/// <param name="stateMachine">The heap-allocated state machine object.</param>
@@ -238,9 +236,7 @@ namespace System.Runtime.CompilerServices
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
- {
- AsyncMethodBuilderCore.Start(ref stateMachine);
- }
+ => AsyncMethodBuilderCore.Start(ref stateMachine);
/// <summary>Associates the builder with the state machine it represents.</summary>
/// <param name="stateMachine">The heap-allocated state machine object.</param>
@@ -437,9 +433,7 @@ namespace System.Runtime.CompilerServices
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
- {
- AsyncMethodBuilderCore.Start(ref stateMachine); // argument validation handled by AsyncMethodBuilderCore
- }
+ => AsyncMethodBuilderCore.Start(ref stateMachine);
/// <summary>Associates the builder with the state machine it represents.</summary>
/// <param name="stateMachine">The heap-allocated state machine object.</param>
@@ -617,7 +611,6 @@ namespace System.Runtime.CompilerServices
// - Boolean
// - Byte, SByte
// - Char
- // - Decimal
// - Int32, UInt32
// - Int64, UInt64
// - Int16, UInt16
@@ -652,7 +645,6 @@ namespace System.Runtime.CompilerServices
(typeof(TResult) == typeof(Byte) && default(Byte) == (Byte)(object)result) ||
(typeof(TResult) == typeof(SByte) && default(SByte) == (SByte)(object)result) ||
(typeof(TResult) == typeof(Char) && default(Char) == (Char)(object)result) ||
- (typeof(TResult) == typeof(Decimal) && default(Decimal) == (Decimal)(object)result) ||
(typeof(TResult) == typeof(Int64) && default(Int64) == (Int64)(object)result) ||
(typeof(TResult) == typeof(UInt64) && default(UInt64) == (UInt64)(object)result) ||
(typeof(TResult) == typeof(Int16) && default(Int16) == (Int16)(object)result) ||
@@ -724,12 +716,32 @@ namespace System.Runtime.CompilerServices
internal static void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
- // Async state machines are required not to throw, so no need for try/finally here.
Thread currentThread = Thread.CurrentThread;
- ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher);
- ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs);
+ ExecutionContext previousExecutionCtx = currentThread.ExecutionContext;
+ SynchronizationContext previousSyncCtx = currentThread.SynchronizationContext;
+
+ // Async state machines are required not to throw, so no need for try/finally here.
stateMachine.MoveNext();
- ecs.Undo(currentThread);
+
+ // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+ if (previousSyncCtx != currentThread.SynchronizationContext)
+ {
+ // Restore changed SynchronizationContext back to previous
+ currentThread.SynchronizationContext = previousSyncCtx;
+ }
+
+ ExecutionContext currentExecutionCtx = currentThread.ExecutionContext;
+ if (previousExecutionCtx != currentExecutionCtx)
+ {
+ // Restore changed ExecutionContext back to previous
+ currentThread.ExecutionContext = previousExecutionCtx;
+ if ((currentExecutionCtx != null && currentExecutionCtx.HasChangeNotifications) ||
+ (previousExecutionCtx != null && previousExecutionCtx.HasChangeNotifications))
+ {
+ // There are change notifications; trigger any affected
+ ExecutionContext.OnValuesChanged(currentExecutionCtx, previousExecutionCtx);
+ }
+ }
}
//
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index d5c425a3b..f126f4f81 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -65,23 +65,23 @@ namespace System.Runtime.CompilerServices
return RuntimeImports.RhMemberwiseClone(obj);
}
- public new static bool Equals(Object obj1, Object obj2)
+ public new static bool Equals(Object o1, Object o2)
{
- if (obj1 == obj2)
+ if (o1 == o2)
return true;
- if ((obj1 == null) || (obj2 == null))
+ if ((o1 == null) || (o2 == null))
return false;
// If it's not a value class, don't compare by value
- if (!obj1.EETypePtr.IsValueType)
+ if (!o1.EETypePtr.IsValueType)
return false;
// Make sure they are the same type.
- if (obj1.EETypePtr != obj2.EETypePtr)
+ if (o1.EETypePtr != o2.EETypePtr)
return false;
- return RuntimeImports.RhCompareObjectContentsAndPadding(obj1, obj2);
+ return RuntimeImports.RhCompareObjectContentsAndPadding(o1, o2);
}
#if !FEATURE_SYNCTABLE
@@ -157,9 +157,6 @@ namespace System.Runtime.CompilerServices
public static int OffsetToStringData
{
- // Workaround to allow WebAssembly to define a size here without a special CoreLib build
- // https://github.com/dotnet/corert/issues/4506 includes removing this.
- [Intrinsic]
get
{
// Number of bytes from the address pointed to by a reference to
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs
index 7bdb228f0..b296c66fd 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs
@@ -19,6 +19,7 @@ namespace System.Runtime.InteropServices
/// in order to be accessible from System.Private.Interop.dll.
/// </summary>
[CLSCompliant(false)]
+ [ReflectionBlocked]
public static class InteropExtensions
{
// Converts a managed DateTime to native OLE datetime
@@ -183,8 +184,7 @@ namespace System.Runtime.InteropServices
public static bool IsDelegate(this RuntimeTypeHandle handle)
{
- return InteropExtensions.AreTypesAssignable(handle, typeof(MulticastDelegate).TypeHandle) ||
- InteropExtensions.AreTypesAssignable(handle, typeof(Delegate).TypeHandle);
+ return InteropExtensions.AreTypesAssignable(handle, typeof(Delegate).TypeHandle);
}
public static bool AreTypesAssignable(RuntimeTypeHandle sourceType, RuntimeTypeHandle targetType)
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
index aaac87eb2..5af9ce674 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Internal.Runtime.Augments;
namespace System.Runtime.InteropServices
{
@@ -26,6 +27,11 @@ namespace System.Runtime.InteropServices
return PInvokeMarshal.GetLastWin32Error();
}
+ public static int GetHRForLastWin32Error()
+ {
+ return PInvokeMarshal.GetHRForLastWin32Error();
+ }
+
public static unsafe IntPtr AllocHGlobal(IntPtr cb)
{
return PInvokeMarshal.AllocHGlobal(cb);
@@ -61,6 +67,17 @@ namespace System.Runtime.InteropServices
{
return PInvokeMarshal.PtrToStringAnsi(ptr);
}
+
+ public static unsafe String PtrToStringAnsi(IntPtr ptr, int len)
+ {
+ return PInvokeMarshal.PtrToStringAnsi(ptr, len);
+ }
#endif
+
+ public static void ThrowExceptionForHR(int errorCode)
+ {
+ if (errorCode < 0)
+ throw RuntimeAugments.Callbacks.GetExceptionForHR(errorCode);
+ }
}
}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs
new file mode 100644
index 000000000..d259223ff
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// Base class for all 'wrapper' classes that wraps a native function pointer
+ /// The forward delegates (that wraps native function pointers) points to derived Invoke method of this
+ /// class, and the Invoke method would implement the marshalling and making the call
+ /// </summary>
+ public abstract class NativeFunctionPointerWrapper
+ {
+ public NativeFunctionPointerWrapper(IntPtr nativeFunctionPointer)
+ {
+ m_nativeFunctionPointer = nativeFunctionPointer;
+ }
+
+ IntPtr m_nativeFunctionPointer;
+
+ public IntPtr NativeFunctionPointer
+ {
+ get { return m_nativeFunctionPointer; }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs
index 8a97ce15a..aeac95a07 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs
@@ -44,6 +44,16 @@ namespace System.Runtime.InteropServices
return System.Text.Encoding.UTF8.GetString((byte*)ptr, len);
}
+ public static unsafe String PtrToStringAnsi(IntPtr ptr, int len)
+ {
+ if (ptr == IntPtr.Zero)
+ throw new ArgumentNullException(nameof(ptr));
+ if (len < 0)
+ throw new ArgumentException(nameof(len));
+
+ return System.Text.Encoding.UTF8.GetString((byte*)ptr, len);
+ }
+
public static unsafe IntPtr MemAlloc(IntPtr cb)
{
return Interop.MemAlloc((UIntPtr)(void*)cb);
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs
index 79c3ed85a..bca69bce3 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs
@@ -40,6 +40,15 @@ namespace System.Runtime.InteropServices
s_lastWin32Error = errorCode;
}
+ public static int GetHRForLastWin32Error()
+ {
+ int dwLastError = GetLastWin32Error();
+ if ((dwLastError & 0x80000000) == 0x80000000)
+ return dwLastError;
+ else
+ return (dwLastError & 0x0000FFFF) | unchecked((int)0x80070000);
+ }
+
public static unsafe IntPtr AllocHGlobal(IntPtr cb)
{
return MemAlloc(cb);
@@ -177,7 +186,7 @@ namespace System.Runtime.InteropServices
/// Return the stub to the pinvoke marshalling stub
/// </summary>
/// <param name="del">The delegate</param>
- public static IntPtr GetStubForPInvokeDelegate(Delegate del)
+ public static IntPtr GetFunctionPointerForDelegate(Delegate del)
{
if (del == null)
return IntPtr.Zero;
@@ -305,16 +314,13 @@ namespace System.Runtime.InteropServices
var delegateThunk = new PInvokeDelegateThunk(del);
- McgPInvokeDelegateData pinvokeDelegateData;
- if (!RuntimeAugments.InteropCallbacks.TryGetMarshallerDataForDelegate(del.GetTypeHandle(), out pinvokeDelegateData))
- {
- Environment.FailFast("Couldn't find marshalling stubs for delegate.");
- }
-
//
// For open static delegates set target to ReverseOpenStaticDelegateStub which calls the static function pointer directly
//
- IntPtr pTarget = del.GetRawFunctionPointerForOpenStaticDelegate() == IntPtr.Zero ? pinvokeDelegateData.ReverseStub : pinvokeDelegateData.ReverseOpenStaticDelegateStub;
+ bool openStaticDelegate = del.GetRawFunctionPointerForOpenStaticDelegate() != IntPtr.Zero;
+
+ IntPtr pTarget = RuntimeAugments.InteropCallbacks.GetDelegateMarshallingStub(del.GetTypeHandle(), openStaticDelegate);
+ Debug.Assert(pTarget != IntPtr.Zero);
RuntimeAugments.SetThunkData(s_thunkPoolHeap, delegateThunk.Thunk, delegateThunk.ContextData, pTarget);
@@ -324,9 +330,9 @@ namespace System.Runtime.InteropServices
/// <summary>
/// Retrieve the corresponding P/invoke instance from the stub
/// </summary>
- public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType)
+ public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, RuntimeTypeHandle delegateType)
{
- if (pStub == IntPtr.Zero)
+ if (ptr == IntPtr.Zero)
return null;
//
// First try to see if this is one of the thunks we've allocated when we marshal a managed
@@ -335,7 +341,7 @@ namespace System.Runtime.InteropServices
//
IntPtr pContext;
IntPtr pTarget;
- if (s_thunkPoolHeap != null && RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, pStub, out pContext, out pTarget))
+ if (s_thunkPoolHeap != null && RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, ptr, out pContext, out pTarget))
{
GCHandle handle;
unsafe
@@ -343,7 +349,7 @@ namespace System.Runtime.InteropServices
// Pull out Handle from context
handle = ((ThunkContextData*)pContext)->Handle;
}
- Delegate target = InteropExtensions.UncheckedCast<Delegate>(handle.Target);
+ Delegate target = Unsafe.As<Delegate>(handle.Target);
//
// The delegate might already been garbage collected
@@ -363,15 +369,10 @@ namespace System.Runtime.InteropServices
// We need to create the delegate that points to the invoke method of a
// NativeFunctionPointerWrapper derived class
//
- McgPInvokeDelegateData pInvokeDelegateData;
- if (!RuntimeAugments.InteropCallbacks.TryGetMarshallerDataForDelegate(delegateType, out pInvokeDelegateData))
- {
- return null;
- }
- return CalliIntrinsics.Call<Delegate>(
- pInvokeDelegateData.ForwardDelegateCreationStub,
- pStub
- );
+ IntPtr pDelegateCreationStub = RuntimeAugments.InteropCallbacks.GetForwardDelegateCreationStub(delegateType);
+ Debug.Assert(pDelegateCreationStub != IntPtr.Zero);
+
+ return CalliIntrinsics.Call<Delegate>(pDelegateCreationStub, ptr);
}
/// <summary>
@@ -420,7 +421,7 @@ namespace System.Runtime.InteropServices
}
- T target = InteropExtensions.UncheckedCast<T>(handle.Target);
+ T target = Unsafe.As<T>(handle.Target);
//
// The delegate might already been garbage collected
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
index 668064865..79f5c2e83 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
@@ -429,7 +429,7 @@ namespace System.Runtime.InteropServices
(fDispose ? StateBits.Disposed : 0);
} while (Interlocked.CompareExchange(ref _state, newState, oldState) != oldState);
- // If we get here we successfully decremented the ref count. Additonally we
+ // If we get here we successfully decremented the ref count. Additionally we
// may have decremented it to zero and set the handle state as closed. In
// this case (providng we own the handle) we will call the ReleaseHandle
// method on the SafeHandle subclass.
diff --git a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
index 65473f205..f8ebc13ee 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -26,19 +26,12 @@ namespace System.Runtime
// E.g., the class and methods are marked internal assuming that only the base class library needs them
// but if a class library wants to factor differently (such as putting the GCHandle methods in an
// optional library, those methods can be moved to a different file/namespace/dll
-
+ [ReflectionBlocked]
public static class RuntimeImports
{
private const string RuntimeLibrary = "[MRT]";
[MethodImpl(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "RhpSetHighLevelDebugFuncEvalHelper")]
- internal static extern void RhpSetHighLevelDebugFuncEvalHelper(IntPtr highLevelDebugFuncEvalHelper);
-
- [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void RhpSetHighLevelDebugFuncEvalAbortHelper(IntPtr highLevelDebugFuncEvalAbortHelper);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpSendCustomEventToDebugger")]
internal static extern void RhpSendCustomEventToDebugger(IntPtr payload, int length);
@@ -340,6 +333,10 @@ namespace System.Runtime
internal static extern unsafe object RhBoxAny(void* pData, EETypePtr pEEType);
[MethodImpl(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhBoxAny")]
+ internal static extern unsafe object RhBoxAny(ref byte pData, EETypePtr pEEType);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhNewObject")]
internal static extern object RhNewObject(EETypePtr pEEType);
@@ -766,25 +763,99 @@ namespace System.Runtime
[RuntimeImport(RuntimeLibrary, "fabs")]
internal static extern double fabs(double x);
+#if PROJECTN
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "floor")]
- internal static extern double floor(double x);
+ [RuntimeImport(RuntimeLibrary, "fabsf")]
+ internal static extern float fabsf(float x);
+#else
+ [Intrinsic]
+ internal static float fabsf(float x)
+ {
+ // fabsf is not a real export for some architectures
+ return (float)fabs(x);
+ }
+#endif
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "fmod")]
- internal static extern double fmod(double x, double y);
+ [RuntimeImport(RuntimeLibrary, "acos")]
+ internal static extern double acos(double x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "pow")]
- internal static extern double pow(double x, double y);
+ [RuntimeImport(RuntimeLibrary, "acosf")]
+ internal static extern float acosf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "sqrt")]
- internal static extern double sqrt(double x);
+ [RuntimeImport(RuntimeLibrary, "acosh")]
+ internal static extern double acosh(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "acoshf")]
+ internal static extern float acoshf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "asin")]
+ internal static extern double asin(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "asinf")]
+ internal static extern float asinf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "asinh")]
+ internal static extern double asinh(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "asinhf")]
+ internal static extern float asinhf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atan")]
+ internal static extern double atan(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atanf")]
+ internal static extern float atanf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atan2")]
+ internal static extern double atan2(double y, double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atan2f")]
+ internal static extern float atan2f(float y, float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atanh")]
+ internal static extern double atanh(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "atanhf")]
+ internal static extern float atanhf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "cbrt")]
+ internal static extern double cbrt(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "cbrtf")]
+ internal static extern float cbrtf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -793,18 +864,18 @@ namespace System.Runtime
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "cos")]
- internal static extern double cos(double x);
+ [RuntimeImport(RuntimeLibrary, "ceilf")]
+ internal static extern float ceilf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "sin")]
- internal static extern double sin(double x);
+ [RuntimeImport(RuntimeLibrary, "cos")]
+ internal static extern double cos(double x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "tan")]
- internal static extern double tan(double x);
+ [RuntimeImport(RuntimeLibrary, "cosf")]
+ internal static extern float cosf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -813,38 +884,38 @@ namespace System.Runtime
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "sinh")]
- internal static extern double sinh(double x);
+ [RuntimeImport(RuntimeLibrary, "coshf")]
+ internal static extern float coshf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "tanh")]
- internal static extern double tanh(double x);
+ [RuntimeImport(RuntimeLibrary, "exp")]
+ internal static extern double exp(double x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "acos")]
- internal static extern double acos(double x);
+ [RuntimeImport(RuntimeLibrary, "expf")]
+ internal static extern float expf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "asin")]
- internal static extern double asin(double x);
+ [RuntimeImport(RuntimeLibrary, "floor")]
+ internal static extern double floor(double x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "atan")]
- internal static extern double atan(double x);
+ [RuntimeImport(RuntimeLibrary, "floorf")]
+ internal static extern float floorf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "atan2")]
- internal static extern double atan2(double x, double y);
+ [RuntimeImport(RuntimeLibrary, "log")]
+ internal static extern double log(double x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "log")]
- internal static extern double log(double x);
+ [RuntimeImport(RuntimeLibrary, "logf")]
+ internal static extern float logf(float x);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -853,14 +924,89 @@ namespace System.Runtime
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- [RuntimeImport(RuntimeLibrary, "exp")]
- internal static extern double exp(double x);
+ [RuntimeImport(RuntimeLibrary, "log10f")]
+ internal static extern float log10f(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "pow")]
+ internal static extern double pow(double x, double y);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "powf")]
+ internal static extern float powf(float x, float y);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sin")]
+ internal static extern double sin(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sinf")]
+ internal static extern float sinf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sinh")]
+ internal static extern double sinh(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sinhf")]
+ internal static extern float sinhf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sqrt")]
+ internal static extern double sqrt(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "sqrtf")]
+ internal static extern float sqrtf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "tan")]
+ internal static extern double tan(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "tanf")]
+ internal static extern float tanf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "tanh")]
+ internal static extern double tanh(double x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "tanhf")]
+ internal static extern float tanhf(float x);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "fmod")]
+ internal static extern double fmod(double x, double y);
+
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "fmodf")]
+ internal static extern float fmodf(float x, float y);
[Intrinsic]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "modf")]
internal static extern unsafe double modf(double x, double* intptr);
+ [Intrinsic]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "modff")]
+ internal static extern unsafe float modff(float x, float* intptr);
+
#if !PLATFORM_UNIX
// ExactSpelling = 'true' to force MCG to resolve it to default
[DllImport(RuntimeImports.RuntimeLibrary, ExactSpelling = true)]
diff --git a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs
index c42a690f5..fe283e992 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
namespace System.Runtime
{
+ [ReflectionBlocked]
public static class TypeLoaderExports
{
#if PROJECTN
@@ -419,6 +420,7 @@ namespace System.Runtime
}
}
+ [ReflectionBlocked]
public delegate IntPtr RuntimeObjectFactory(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult);
[System.Runtime.InteropServices.McgIntrinsicsAttribute]
diff --git a/src/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs b/src/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs
index 917ef82d9..2a95e2d25 100644
--- a/src/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs
+++ b/src/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
namespace System
{
[StructLayout(LayoutKind.Sequential)]
- public struct RuntimeArgumentHandle
+ public ref struct RuntimeArgumentHandle
{
}
}
diff --git a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs
index bc12ca38b..ec40c126d 100644
--- a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs
+++ b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs
@@ -24,6 +24,7 @@ namespace System
}
}
+ [ReflectionBlocked]
public class RuntimeExceptionHelpers
{
//------------------------------------------------------------------------------------------------------------
@@ -200,7 +201,7 @@ namespace System
internal static void FailFast(string message, Exception exception, RhFailFastReason reason, IntPtr pExAddress, IntPtr pExContext)
{
- // If this a recursive call to FailFast, avoid all unnecessary and complex actitivy the second time around to avoid the recursion
+ // If this a recursive call to FailFast, avoid all unnecessary and complex activity the second time around to avoid the recursion
// that got us here the first time (Some judgement is required as to what activity is "unnecessary and complex".)
bool minimalFailFast = InFailFast.Value || (exception is OutOfMemoryException);
InFailFast.Value = true;
@@ -248,9 +249,9 @@ namespace System
#pragma warning disable 414 // field is assigned, but never used -- This is because C# doesn't realize that we
// copy the field into a buffer.
/// <summary>
- /// This is the header that describes our 'error report' buffer to the minidump auxillary provider.
+ /// This is the header that describes our 'error report' buffer to the minidump auxiliary provider.
/// Its format is know to that system-wide DLL, so do not change it. The remainder of the buffer is
- /// opaque to the minidump auxillary provider, so it'll have its own format that is more easily
+ /// opaque to the minidump auxiliary provider, so it'll have its own format that is more easily
/// changed.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
@@ -361,7 +362,7 @@ namespace System
/// <summary>
/// This method will call the runtime to gather the Exception objects from every exception dispatch in
/// progress on the current thread. It will then serialize them into a new buffer and pass that
- /// buffer back to the runtime, which will publish it to a place where a global "minidump auxillary
+ /// buffer back to the runtime, which will publish it to a place where a global "minidump auxiliary
/// provider" will be able to save the buffer's contents into triage dumps.
///
/// Thread safety information: The guarantee of this method is that the buffer it produces will have
diff --git a/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs b/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs
index 844447a93..dac2f2958 100644
--- a/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs
+++ b/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs
@@ -31,6 +31,9 @@ namespace System
if (_value == handle._value)
return true;
+ if (_value == IntPtr.Zero || handle._value == IntPtr.Zero)
+ return false;
+
string fieldName1, fieldName2;
RuntimeTypeHandle declaringType1, declaringType2;
@@ -48,6 +51,9 @@ namespace System
public override int GetHashCode()
{
+ if (_value == IntPtr.Zero)
+ return 0;
+
string fieldName;
RuntimeTypeHandle declaringType;
RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(this, out declaringType, out fieldName);
diff --git a/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs
index ba276010e..c079f4756 100644
--- a/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs
+++ b/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs
@@ -33,6 +33,9 @@ namespace System
if (_value == handle._value)
return true;
+ if (_value == IntPtr.Zero || handle._value == IntPtr.Zero)
+ return false;
+
RuntimeTypeHandle declaringType1, declaringType2;
MethodNameAndSignature nameAndSignature1, nameAndSignature2;
RuntimeTypeHandle[] genericArgs1, genericArgs2;
@@ -68,6 +71,9 @@ namespace System
public override int GetHashCode()
{
+ if (_value == IntPtr.Zero)
+ return 0;
+
RuntimeTypeHandle declaringType;
MethodNameAndSignature nameAndSignature;
RuntimeTypeHandle[] genericArgs;
diff --git a/src/System.Private.CoreLib/src/System/String.Comparison.cs b/src/System.Private.CoreLib/src/System/String.Comparison.cs
index b5d3c1787..914fa6aa1 100644
--- a/src/System.Private.CoreLib/src/System/String.Comparison.cs
+++ b/src/System.Private.CoreLib/src/System/String.Comparison.cs
@@ -5,9 +5,16 @@
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
namespace System
{
public partial class String
@@ -427,23 +434,21 @@ namespace System
// for meaning of different comparisonType.
public static int Compare(String strA, String strB, StringComparison comparisonType)
{
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- {
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
- }
-
if (object.ReferenceEquals(strA, strB))
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return 0;
}
// They can't both be null at this point.
if (strA == null)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return -1;
}
if (strB == null)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return 1;
}
@@ -470,13 +475,13 @@ namespace System
return CompareInfo.CompareOrdinalIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
+ return CompareInfo.Invariant.Compare(strA, strB, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.Compare(strA, strB, CompareOptions.IgnoreCase);
default:
- throw new NotSupportedException(SR.NotSupported_StringComparison);
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
}
}
@@ -596,10 +601,7 @@ namespace System
public static int Compare(String strA, int indexA, String strB, int indexB, int length, StringComparison comparisonType)
{
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- {
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
- }
+ StringSpanHelpers.CheckStringComparison(comparisonType);
if (strA == null || strB == null)
{
@@ -653,13 +655,13 @@ namespace System
return CompareInfo.CompareOrdinalIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
+ return CompareInfo.Invariant.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
default:
- throw new ArgumentException(SR.NotSupported_StringComparison);
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
}
}
@@ -696,19 +698,36 @@ namespace System
// TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly?
internal static int CompareOrdinal(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
{
- // TODO: This needs to be optimized / unrolled. It can't just use CompareOrdinalHelper(str, str)
- // (changed to accept spans) because its implementation is based on a string layout,
- // in a way that doesn't work when there isn't guaranteed to be a null terminator.
+ // TODO: Add a vectorized code path, similar to SequenceEqual
+ // https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/SpanHelpers.byte.cs#L900
int minLength = Math.Min(strA.Length, strB.Length);
- for (int i = 0; i < minLength; i++)
+ ref char first = ref MemoryMarshal.GetReference(strA);
+ ref char second = ref MemoryMarshal.GetReference(strB);
+
+ int i = 0;
+ if (minLength >= sizeof(nuint) / sizeof(char))
{
- if (strA[i] != strB[i])
+ while (i < minLength - sizeof(nuint) / sizeof(char))
{
- return strA[i] - strB[i];
+ if (Unsafe.ReadUnaligned<nuint>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
+ Unsafe.ReadUnaligned<nuint>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, i))))
+ {
+ break;
+ }
+ i += sizeof(nuint) / sizeof(char);
}
}
-
+ while (i < minLength)
+ {
+ char a = Unsafe.Add(ref first, i);
+ char b = Unsafe.Add(ref second, i);
+ if (a != b)
+ {
+ return a - b;
+ }
+ i++;
+ }
return strA.Length - strB.Length;
}
@@ -804,18 +823,15 @@ namespace System
throw new ArgumentNullException(nameof(value));
}
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- {
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
- }
-
if ((Object)this == (Object)value)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
if (value.Length == 0)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
@@ -834,10 +850,10 @@ namespace System
return this.Length < value.Length ? false : (CompareInfo.CompareOrdinalIgnoreCase(this, this.Length - value.Length, value.Length, value, 0, value.Length) == 0);
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.IsSuffix(this, value, CompareOptions.None);
+ return CompareInfo.Invariant.IsSuffix(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.IsSuffix(this, value, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.IsSuffix(this, value, CompareOptions.IgnoreCase);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
@@ -912,16 +928,15 @@ namespace System
public bool Equals(String value, StringComparison comparisonType)
{
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
-
if ((Object)this == (Object)value)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
if ((Object)value == null)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return false;
}
@@ -947,10 +962,10 @@ namespace System
}
case StringComparison.InvariantCulture:
- return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0);
+ return (CompareInfo.Invariant.Compare(this, value, CompareOptions.None) == 0);
case StringComparison.InvariantCultureIgnoreCase:
- return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0);
+ return (CompareInfo.Invariant.Compare(this, value, CompareOptions.IgnoreCase) == 0);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
@@ -976,16 +991,15 @@ namespace System
public static bool Equals(String a, String b, StringComparison comparisonType)
{
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
-
if ((Object)a == (Object)b)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
if ((Object)a == null || (Object)b == null)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return false;
}
@@ -1011,10 +1025,10 @@ namespace System
}
case StringComparison.InvariantCulture:
- return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0);
+ return (CompareInfo.Invariant.Compare(a, b, CompareOptions.None) == 0);
case StringComparison.InvariantCultureIgnoreCase:
- return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0);
+ return (CompareInfo.Invariant.Compare(a, b, CompareOptions.IgnoreCase) == 0);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
@@ -1119,18 +1133,15 @@ namespace System
throw new ArgumentNullException(nameof(value));
}
- if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
- {
- throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
- }
-
if ((Object)this == (Object)value)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
if (value.Length == 0)
{
+ StringSpanHelpers.CheckStringComparison(comparisonType);
return true;
}
@@ -1159,10 +1170,10 @@ namespace System
return CompareInfo.CompareOrdinalIgnoreCase(this, 0, value.Length, value, 0, value.Length) == 0;
case StringComparison.InvariantCulture:
- return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
+ return CompareInfo.Invariant.IsPrefix(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
- return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
+ return CompareInfo.Invariant.IsPrefix(this, value, CompareOptions.IgnoreCase);
default:
throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
diff --git a/src/System.Private.CoreLib/src/System/String.CoreRT.cs b/src/System.Private.CoreLib/src/System/String.CoreRT.cs
new file mode 100644
index 000000000..a892006d9
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/String.CoreRT.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Text;
+
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ [StructLayout(LayoutKind.Sequential)]
+ [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute]
+ [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public partial class String
+ {
+#if BIT64
+ private const int POINTER_SIZE = 8;
+#else
+ private const int POINTER_SIZE = 4;
+#endif
+ // m_pEEType + _stringLength
+ internal const int FIRST_CHAR_OFFSET = POINTER_SIZE + sizeof(int);
+
+ // CS0169: The private field '{blah}' is never used
+ // CS0649: Field '{blah}' is never assigned to, and will always have its default value
+#pragma warning disable 169, 649
+
+#if PROJECTN
+ [Bound]
+#endif
+ // WARNING: We allow diagnostic tools to directly inspect these two members (_stringLength, _firstChar)
+ // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
+ // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
+ // Get in touch with the diagnostics team if you have questions.
+ [NonSerialized]
+ private int _stringLength;
+ [NonSerialized]
+ private char _firstChar;
+
+#pragma warning restore
+
+ public static readonly String Empty = "";
+
+ // Gets the character at a specified position.
+ //
+ // Spec#: Apply the precondition here using a contract assembly. Potential perf issue.
+ [System.Runtime.CompilerServices.IndexerName("Chars")]
+ public unsafe char this[int index]
+ {
+#if PROJECTN
+ [BoundsChecking]
+ get
+ {
+ return Unsafe.Add(ref _firstChar, index);
+ }
+#else
+ [Intrinsic]
+ get
+ {
+ if ((uint)index >= _stringLength)
+ ThrowHelper.ThrowIndexOutOfRangeException();
+ return Unsafe.Add(ref _firstChar, index);
+ }
+#endif
+ }
+
+ internal static String FastAllocateString(int length)
+ {
+ // We allocate one extra char as an interop convenience so that our strings are null-
+ // terminated, however, we don't pass the extra +1 to the string allocation because the base
+ // size of this object includes the _firstChar field.
+ string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf<string>(), length);
+ Debug.Assert(newStr._stringLength == length);
+ return newStr;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/String.cs b/src/System.Private.CoreLib/src/System/String.cs
index 076bd02a8..6c0f7532b 100644
--- a/src/System.Private.CoreLib/src/System/String.cs
+++ b/src/System.Private.CoreLib/src/System/String.cs
@@ -74,38 +74,9 @@ namespace System
// constructed itself depends on this class also being eagerly constructed. Plus, it's nice to have this
// eagerly constructed to avoid the cost of defered ctors. I can't imagine any app that doesn't use string
//
- [StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute]
[Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public sealed partial class String : IComparable, IEnumerable, IEnumerable<char>, IComparable<String>, IEquatable<String>, IConvertible, ICloneable
{
-#if BIT64
- private const int POINTER_SIZE = 8;
-#else
- private const int POINTER_SIZE = 4;
-#endif
- // m_pEEType + _stringLength
- internal const int FIRST_CHAR_OFFSET = POINTER_SIZE + sizeof(int);
-
- // CS0169: The private field '{blah}' is never used
- // CS0649: Field '{blah}' is never assigned to, and will always have its default value
-#pragma warning disable 169, 649
-
-#if PROJECTN
- [Bound]
-#endif
- // WARNING: We allow diagnostic tools to directly inspect these two members (_stringLength, _firstChar)
- // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
- // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
- // Get in touch with the diagnostics team if you have questions.
- [NonSerialized]
- private int _stringLength;
- [NonSerialized]
- private char _firstChar;
-
-#pragma warning restore
-
// String constructors
// These are special. the implementation methods for these have a different signature from the
// declared constructors.
@@ -464,31 +435,6 @@ namespace System
return result;
}
- public static readonly String Empty = "";
-
- // Gets the character at a specified position.
- //
- // Spec#: Apply the precondition here using a contract assembly. Potential perf issue.
- [System.Runtime.CompilerServices.IndexerName("Chars")]
- public unsafe char this[int index]
- {
-#if PROJECTN
- [BoundsChecking]
- get
- {
- return Unsafe.Add(ref _firstChar, index);
- }
-#else
- [Intrinsic]
- get
- {
- if ((uint)index >= _stringLength)
- ThrowHelper.ThrowIndexOutOfRangeException();
- return Unsafe.Add(ref _firstChar, index);
- }
-#endif
- }
-
// Converts a substring of this string to an array of characters. Copies the
// characters of this string beginning at position sourceIndex and ending at
// sourceIndex + count - 1 to the character array buffer, beginning
@@ -630,16 +576,6 @@ namespace System
return result;
}
- internal static String FastAllocateString(int length)
- {
- // We allocate one extra char as an interop convenience so that our strings are null-
- // terminated, however, we don't pass the extra +1 to the string allocation because the base
- // size of this object includes the _firstChar field.
- string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf<string>(), length);
- Debug.Assert(newStr._stringLength == length);
- return newStr;
- }
-
internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
{
Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
diff --git a/src/System.Private.CoreLib/src/System/Threading/Condition.cs b/src/System.Private.CoreLib/src/System/Threading/Condition.cs
index b854aa946..7dffcb418 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Condition.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Condition.cs
@@ -9,6 +9,7 @@ using System.Diagnostics;
namespace System.Threading
{
+ [System.Runtime.CompilerServices.ReflectionBlocked]
public sealed class Condition
{
internal class Waiter
diff --git a/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs b/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs
index 28b938ab2..2800fc951 100644
--- a/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs
@@ -19,16 +19,16 @@ namespace System.Threading
private static void VerifyNameForCreate(string name)
{
- if (null != name && ((int)Interop.Constants.MaxPath) < name.Length)
+ if (null != name && Interop.Kernel32.MAX_PATH < name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
}
private void CreateEventCore(bool initialState, EventResetMode mode, string name, out bool createdNew)
{
Debug.Assert((mode == EventResetMode.AutoReset) || (mode == EventResetMode.ManualReset));
- Debug.Assert(name == null || name.Length <= (int)Interop.Constants.MaxPath);
+ Debug.Assert(name == null || name.Length <= Interop.Kernel32.MAX_PATH);
uint eventFlags = initialState ? (uint)Interop.Constants.CreateEventInitialSet : 0;
if (mode == EventResetMode.ManualReset)
@@ -71,9 +71,9 @@ namespace System.Threading
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}
- if (null != name && ((int)Interop.Constants.MaxPath) < name.Length)
+ if (null != name && Interop.Kernel32.MAX_PATH < name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
result = null;
diff --git a/src/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/System.Private.CoreLib/src/System/Threading/Lock.cs
index 9669b21eb..2150a6933 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Lock.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Lock.cs
@@ -9,6 +9,7 @@ using Internal.Runtime.Augments;
namespace System.Threading
{
+ [ReflectionBlocked]
public sealed class Lock
{
// The following constants define characteristics of spinning logic in the Lock class
diff --git a/src/System.Private.CoreLib/src/System/Threading/LockHolder.cs b/src/System.Private.CoreLib/src/System/Threading/LockHolder.cs
index 8c2b167a9..eea076390 100644
--- a/src/System.Private.CoreLib/src/System/Threading/LockHolder.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/LockHolder.cs
@@ -6,6 +6,7 @@ using System.Runtime.CompilerServices;
namespace System.Threading
{
+ [ReflectionBlocked]
public struct LockHolder : IDisposable
{
private Lock _lock;
diff --git a/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs b/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
index 2b3c25061..ba5e86304 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
@@ -14,15 +14,15 @@ namespace System.Threading
private static void VerifyNameForCreate(string name)
{
- if (null != name && ((int)Interop.Constants.MaxPath) < name.Length)
+ if (null != name && Interop.Kernel32.MAX_PATH < name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
}
private void CreateMutexCore(bool initiallyOwned, string name, out bool createdNew)
{
- Debug.Assert(name == null || name.Length <= (int)Interop.Constants.MaxPath);
+ Debug.Assert(name == null || name.Length <= Interop.Kernel32.MAX_PATH);
uint mutexFlags = initiallyOwned ? (uint)Interop.Constants.CreateMutexInitialOwner : 0;
@@ -52,9 +52,9 @@ namespace System.Threading
{
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}
- if (((int)Interop.Constants.MaxPath) < (uint)name.Length)
+ if (Interop.Kernel32.MAX_PATH < (uint)name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
result = null;
diff --git a/src/System.Private.CoreLib/src/System/Threading/PinnableBufferCache.cs b/src/System.Private.CoreLib/src/System/Threading/PinnableBufferCache.cs
index c85d532d0..5ebeb4441 100644
--- a/src/System.Private.CoreLib/src/System/Threading/PinnableBufferCache.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/PinnableBufferCache.cs
@@ -401,9 +401,9 @@ namespace System.Threading
else if (m_restockSize < 256)
m_restockSize = m_restockSize * 2; // Grow quickly at small sizes
else if (m_restockSize < 4096)
- m_restockSize = m_restockSize * 3 / 2; // Less agressively at large ones
+ m_restockSize = m_restockSize * 3 / 2; // Less aggressively at large ones
else
- m_restockSize = 4096; // Cap how agressive we are
+ m_restockSize = 4096; // Cap how aggressive we are
// Ensure we hit our minimums
if (m_minBufferCount > m_buffersUnderManagement)
diff --git a/src/System.Private.CoreLib/src/System/Threading/Semaphore.Windows.cs b/src/System.Private.CoreLib/src/System/Threading/Semaphore.Windows.cs
index 67eec809a..3eab7f5d2 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Semaphore.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Semaphore.Windows.cs
@@ -19,9 +19,9 @@ namespace System.Threading
private static void VerifyNameForCreate(string name)
{
- if (null != name && MAX_PATH < name.Length)
+ if (null != name && Interop.Kernel32.MAX_PATH < name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
}
@@ -30,7 +30,7 @@ namespace System.Threading
Debug.Assert(initialCount >= 0);
Debug.Assert(maximumCount >= 1);
Debug.Assert(initialCount <= maximumCount);
- Debug.Assert(name == null || name.Length <= MAX_PATH);
+ Debug.Assert(name == null || name.Length <= Interop.Kernel32.MAX_PATH);
SafeWaitHandle myHandle = Interop.mincore.CreateSemaphoreEx(IntPtr.Zero, initialCount, maximumCount, name, 0, AccessRights);
@@ -64,9 +64,9 @@ namespace System.Threading
{
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}
- if (null != name && MAX_PATH < name.Length)
+ if (null != name && Interop.Kernel32.MAX_PATH < name.Length)
{
- throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, MAX_PATH), nameof(name));
+ throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Kernel32.MAX_PATH), nameof(name));
}
result = null;
diff --git a/src/System.Private.CoreLib/src/System/Threading/Semaphore.cs b/src/System.Private.CoreLib/src/System/Threading/Semaphore.cs
index ddc3c54f1..9e7a7796a 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Semaphore.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Semaphore.cs
@@ -13,8 +13,6 @@ namespace System.Threading
{
public sealed partial class Semaphore : WaitHandle
{
- private const int MAX_PATH = (int)Interop.Constants.MaxPath;
-
// creates a nameless semaphore object
// Win32 only takes maximum count of Int32.MaxValue
public Semaphore(int initialCount, int maximumCount)
diff --git a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs b/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs
index f0305ad04..f949896e2 100644
--- a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs
@@ -321,7 +321,7 @@ namespace System.Threading
// In this case there are three ways to acquire the lock
// 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2
// 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn
- // the late the thread arrives the more it spins and less frequent it check the lock avilability
+ // the late the thread arrives the more it spins and less frequent it check the lock availability
// Also the spins count is increases each iteration
// If the spins iterations finished and failed to acquire the lock, go to step 3
// 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1)
@@ -337,19 +337,19 @@ namespace System.Threading
{
if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
{
- // Aquired lock
+ // Acquired lock
return;
}
if (millisecondsTimeout == 0)
{
- // Did not aquire lock in CompareExchange and timeout is 0 so fail fast
+ // Did not acquire lock in CompareExchange and timeout is 0 so fail fast
return;
}
}
else if (millisecondsTimeout == 0)
{
- // Did not aquire lock as owned and timeout is 0 so fail fast
+ // Did not acquire lock as owned and timeout is 0 so fail fast
return;
}
else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow
diff --git a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs
index e83cdce77..c41b37753 100644
--- a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs
@@ -691,24 +691,20 @@ namespace System.Threading
void ExecuteWorkItem();
}
- internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
+ internal abstract class QueueUserWorkItemCallbackBase : IThreadPoolWorkItem
{
- private WaitCallback callback;
- private readonly ExecutionContext context;
- private readonly Object state;
-
#if DEBUG
private volatile int executed;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1821:RemoveEmptyFinalizers")]
- ~QueueUserWorkItemCallback()
+ ~QueueUserWorkItemCallbackBase()
{
Debug.Assert(
executed != 0 || Environment.HasShutdownStarted /*|| AppDomain.CurrentDomain.IsFinalizingForUnload()*/,
"A QueueUserWorkItemCallback was never called!");
}
- private void MarkExecuted()
+ protected void MarkExecuted()
{
GC.SuppressFinalize(this);
Debug.Assert(
@@ -717,28 +713,51 @@ namespace System.Threading
}
#endif
- internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
- {
- callback = waitCallback;
- state = stateObj;
- context = ec;
- }
-
- void IThreadPoolWorkItem.ExecuteWorkItem()
+ public virtual void ExecuteWorkItem()
{
#if DEBUG
MarkExecuted();
#endif
+ }
+ }
+
+ internal sealed class QueueUserWorkItemCallback : QueueUserWorkItemCallbackBase
+ {
+ private WaitCallback _callback;
+ private readonly object _state;
+ private readonly ExecutionContext _context;
+
+ internal static readonly ContextCallback s_executionContextShim = state =>
+ {
+ var obj = (QueueUserWorkItemCallback)state;
+ WaitCallback c = obj._callback;
+ Debug.Assert(c != null);
+ obj._callback = null;
+ c(obj._state);
+ };
+
+ internal QueueUserWorkItemCallback(WaitCallback callback, object state, ExecutionContext context)
+ {
+ _callback = callback;
+ _state = state;
+ _context = context;
+ }
+
+ public override void ExecuteWorkItem()
+ {
+ base.ExecuteWorkItem();
try
{
- if (context == null)
+ if (_context == null)
{
- WaitCallback cb = callback;
- callback = null;
- cb(state);
+ WaitCallback c = _callback;
+ _callback = null;
+ c(_state);
}
else
- ExecutionContext.Run(context, ccb, this);
+ {
+ ExecutionContext.Run(_context, s_executionContextShim, this);
+ }
}
catch (Exception e)
{
@@ -746,57 +765,80 @@ namespace System.Threading
throw; //unreachable
}
}
-
- internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
-
- private static void WaitCallback_Context(Object state)
- {
- QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
- WaitCallback wc = obj.callback;
- Debug.Assert(null != wc);
- wc(obj.state);
- }
}
- internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem
+ internal sealed class QueueUserWorkItemCallback<TState> : QueueUserWorkItemCallbackBase
{
- private WaitCallback callback;
- private readonly Object state;
+ private Action<TState> _callback;
+ private readonly TState _state;
+ private readonly ExecutionContext _context;
-#if DEBUG
- private volatile int executed;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1821:RemoveEmptyFinalizers")]
- ~QueueUserWorkItemCallbackDefaultContext()
+ internal static readonly ContextCallback s_executionContextShim = state =>
{
- Debug.Assert(
- executed != 0 || Environment.HasShutdownStarted /*|| AppDomain.CurrentDomain.IsFinalizingForUnload()*/,
- "A QueueUserWorkItemCallbackDefaultContext was never called!");
+ var obj = (QueueUserWorkItemCallback<TState>)state;
+ Action<TState> c = obj._callback;
+ Debug.Assert(c != null);
+ obj._callback = null;
+ c(obj._state);
+ };
+
+ internal QueueUserWorkItemCallback(Action<TState> callback, TState state, ExecutionContext context)
+ {
+ _callback = callback;
+ _state = state;
+ _context = context;
}
- private void MarkExecuted()
+ public override void ExecuteWorkItem()
{
- GC.SuppressFinalize(this);
- Debug.Assert(
- 0 == Interlocked.Exchange(ref executed, 1),
- "A QueueUserWorkItemCallbackDefaultContext was called twice!");
+ base.ExecuteWorkItem();
+ try
+ {
+ if (_context == null)
+ {
+ Action<TState> c = _callback;
+ _callback = null;
+ c(_state);
+ }
+ else
+ {
+ ExecutionContext.RunInternal(_context, s_executionContextShim, this);
+ }
+ }
+ catch (Exception e)
+ {
+ RuntimeAugments.ReportUnhandledException(e);
+ throw; //unreachable
+ }
}
-#endif
+ }
+
+ internal sealed class QueueUserWorkItemCallbackDefaultContext : QueueUserWorkItemCallbackBase
+ {
+ private WaitCallback _callback;
+ private readonly object _state;
- internal QueueUserWorkItemCallbackDefaultContext(WaitCallback waitCallback, Object stateObj)
+ internal static readonly ContextCallback s_executionContextShim = state =>
{
- callback = waitCallback;
- state = stateObj;
+ var obj = (QueueUserWorkItemCallbackDefaultContext)state;
+ WaitCallback c = obj._callback;
+ Debug.Assert(c != null);
+ obj._callback = null;
+ c(obj._state);
+ };
+
+ internal QueueUserWorkItemCallbackDefaultContext(WaitCallback callback, object state)
+ {
+ _callback = callback;
+ _state = state;
}
- void IThreadPoolWorkItem.ExecuteWorkItem()
+ public override void ExecuteWorkItem()
{
-#if DEBUG
- MarkExecuted();
-#endif
+ base.ExecuteWorkItem();
try
{
- ExecutionContext.Run(ExecutionContext.Default, ccb, this);
+ ExecutionContext.Run(ExecutionContext.Default, s_executionContextShim, this);
}
catch (Exception e)
{
@@ -804,16 +846,40 @@ namespace System.Threading
throw; //unreachable
}
}
+ }
+
+ internal sealed class QueueUserWorkItemCallbackDefaultContext<TState> : QueueUserWorkItemCallbackBase
+ {
+ private Action<TState> _callback;
+ private readonly TState _state;
- internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
+ internal static readonly ContextCallback s_executionContextShim = state =>
+ {
+ var obj = (QueueUserWorkItemCallbackDefaultContext<TState>)state;
+ Action<TState> c = obj._callback;
+ Debug.Assert(c != null);
+ obj._callback = null;
+ c(obj._state);
+ };
+
+ internal QueueUserWorkItemCallbackDefaultContext(Action<TState> callback, TState state)
+ {
+ _callback = callback;
+ _state = state;
+ }
- private static void WaitCallback_Context(Object state)
+ public override void ExecuteWorkItem()
{
- QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state;
- WaitCallback wc = obj.callback;
- Debug.Assert(null != wc);
- obj.callback = null;
- wc(obj.state);
+ base.ExecuteWorkItem();
+ try
+ {
+ ExecutionContext.Run(ExecutionContext.Default, s_executionContextShim, this);
+ }
+ catch (Exception e)
+ {
+ RuntimeAugments.ReportUnhandledException(e);
+ throw; //unreachable
+ }
}
}
@@ -965,12 +1031,9 @@ namespace System.Threading
}
public static bool QueueUserWorkItem(WaitCallback callBack) =>
- QueueUserWorkItem(callBack, null, preferLocal: false);
-
- public static bool QueueUserWorkItem(WaitCallback callBack, object state) =>
- QueueUserWorkItem(callBack, state, preferLocal: false);
+ QueueUserWorkItem(callBack, null);
- public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool preferLocal)
+ public static bool QueueUserWorkItem(WaitCallback callBack, object state)
{
if (callBack == null)
{
@@ -983,6 +1046,24 @@ namespace System.Threading
new QueueUserWorkItemCallbackDefaultContext(callBack, state) :
(IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context);
+ ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true);
+
+ return true;
+ }
+
+ public static bool QueueUserWorkItem<TState>(Action<TState> callBack, TState state, bool preferLocal)
+ {
+ if (callBack == null)
+ {
+ throw new ArgumentNullException(nameof(callBack));
+ }
+
+ ExecutionContext context = ExecutionContext.Capture();
+
+ IThreadPoolWorkItem tpcallBack = context == ExecutionContext.Default ?
+ new QueueUserWorkItemCallbackDefaultContext<TState>(callBack, state) :
+ (IThreadPoolWorkItem)new QueueUserWorkItemCallback<TState>(callBack, state, context);
+
ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal);
return true;
diff --git a/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs b/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs
index f082a5d1d..1d7367d93 100644
--- a/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs
@@ -65,7 +65,7 @@ namespace System.Threading
/// - A signaler iterates over waiters and tries to release waiters based on the signal count
/// - For each waiter, the signaler checks if the waiter's wait can be terminated
/// - When a waiter's wait can be terminated, the signaler does everything necesary before waking the waiter, such that
- /// the waiter can simply continue after awakening, including unregistering the wait and assigining ownership if
+ /// the waiter can simply continue after awakening, including unregistering the wait and assigning ownership if
/// applicable
/// - Interrupting
/// - Interrupting is just another way of signaling a waiting thread. The interrupter unregisters the wait and wakes the
diff --git a/src/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/System.Private.CoreLib/src/System/ThrowHelper.cs
index ff7bb2b4b..a3e3a0602 100644
--- a/src/System.Private.CoreLib/src/System/ThrowHelper.cs
+++ b/src/System.Private.CoreLib/src/System/ThrowHelper.cs
@@ -93,6 +93,10 @@ namespace System
{
throw new ArgumentException(SR.Argument_DestinationTooShort);
}
+ internal static void ThrowArgumentException_OverlapAlignmentMismatch()
+ {
+ throw new ArgumentException(SR.Argument_OverlapAlignmentMismatch);
+ }
internal static void ThrowArgumentOutOfRange_IndexException()
{
throw GetArgumentOutOfRangeException(ExceptionArgument.index,
@@ -164,14 +168,14 @@ namespace System
throw new ArgumentNullException(GetArgumentName(argument));
}
- internal static void ThrowObjectDisposedException(string objectName, ExceptionResource resource)
+ internal static void ThrowInvalidOperationException(ExceptionResource resource)
{
- throw new ObjectDisposedException(objectName, GetResourceString(resource));
+ throw new InvalidOperationException(GetResourceString(resource));
}
- internal static void ThrowInvalidOperationException(ExceptionResource resource)
+ internal static void ThrowInvalidOperationException_OutstandingReferences()
{
- throw new InvalidOperationException(GetResourceString(resource));
+ throw new InvalidOperationException(SR.Memory_OutstandingReferences);
}
internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
@@ -204,6 +208,11 @@ namespace System
throw new SerializationException(GetResourceString(resource));
}
+ internal static void ThrowObjectDisposedException_MemoryDisposed()
+ {
+ throw new ObjectDisposedException("OwnedMemory<T>", SR.MemoryDisposed);
+ }
+
internal static void ThrowNotSupportedException()
{
throw new NotSupportedException();
@@ -302,6 +311,16 @@ namespace System
return "start";
case ExceptionArgument.format:
return "format";
+ case ExceptionArgument.culture:
+ return "culture";
+ case ExceptionArgument.comparer:
+ return "comparer";
+ case ExceptionArgument.comparable:
+ return "comparable";
+ case ExceptionArgument.source:
+ return "source";
+ case ExceptionArgument.state:
+ return "state";
default:
Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum.");
return "";
@@ -350,10 +369,6 @@ namespace System
return SR.TaskCompletionSourceT_TrySetException_NullException;
case ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions:
return SR.TaskCompletionSourceT_TrySetException_NoExceptions;
- case ExceptionResource.Memory_ThrowIfDisposed:
- return SR.Memory_ThrowIfDisposed;
- case ExceptionResource.Memory_OutstandingReferences:
- return SR.Memory_OutstandingReferences;
default:
Debug.Assert(false,
"The enum value is not defined, please check the ExceptionResource Enum.");
@@ -394,7 +409,12 @@ namespace System
exception,
pointer,
start,
- format
+ format,
+ culture,
+ comparer,
+ comparable,
+ source,
+ state
}
//
@@ -421,7 +441,5 @@ namespace System
TaskT_TransitionToFinal_AlreadyCompleted,
TaskCompletionSourceT_TrySetException_NullException,
TaskCompletionSourceT_TrySetException_NoExceptions,
- Memory_ThrowIfDisposed,
- Memory_OutstandingReferences,
}
}
diff --git a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs
deleted file mode 100644
index 8e85f21ed..000000000
--- a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs
+++ /dev/null
@@ -1,933 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose:
-** This class is used to represent a Dynamic TimeZone. It
-** has methods for converting a DateTime between TimeZones
-**
-**
-============================================================*/
-
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Globalization;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-namespace System
-{
- sealed public partial class TimeZoneInfo
- {
- // use for generating multi-year DST periods
- private static readonly TransitionTime s_transition5_15 = TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1), 05, 15);
- private static readonly TransitionTime s_transition7_15 = TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1), 07, 15);
- private static readonly TransitionTime s_transition10_15 = TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1), 10, 15);
- private static readonly TransitionTime s_transition12_15 = TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1), 12, 15);
-
- private TimeZoneInfo(Byte[] data, Boolean dstDisabled)
- {
- TZifHead t;
- DateTime[] dts;
- Byte[] typeOfLocalTime;
- TZifType[] transitionType;
- String zoneAbbreviations;
- Boolean[] StandardTime;
- Boolean[] GmtTime;
-
- // parse the raw TZif bytes; this method can throw ArgumentException when the data is malformed.
- TZif_ParseRaw(data, out t, out dts, out typeOfLocalTime, out transitionType, out zoneAbbreviations, out StandardTime, out GmtTime);
-
- _id = c_localId;
- _displayName = c_localId;
- _baseUtcOffset = TimeSpan.Zero;
-
- // find the best matching baseUtcOffset and display strings based on the current utcNow value
- DateTime utcNow = DateTime.UtcNow;
- for (int i = 0; i < dts.Length && dts[i] <= utcNow; i++)
- {
- int type = typeOfLocalTime[i];
- if (!transitionType[type].IsDst)
- {
- _baseUtcOffset = transitionType[type].UtcOffset;
- _standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
- }
- else
- {
- _daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
- }
- }
-
- if (dts.Length == 0)
- {
- // time zones like Africa/Bujumbura and Etc/GMT* have no transition times but still contain
- // TZifType entries that may contain a baseUtcOffset and display strings
- for (int i = 0; i < transitionType.Length; i++)
- {
- if (!transitionType[i].IsDst)
- {
- _baseUtcOffset = transitionType[i].UtcOffset;
- _standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
- }
- else
- {
- _daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
- }
- }
- }
- _id = _standardDisplayName;
- _displayName = _standardDisplayName;
-
- // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
- // with DateTimeOffset, SQL Server, and the W3C XML Specification
- if (_baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
- {
- _baseUtcOffset = new TimeSpan(_baseUtcOffset.Hours, _baseUtcOffset.Minutes, 0);
- }
-
- if (!dstDisabled)
- {
- // only create the adjustment rule if DST is enabled
- TZif_GenerateAdjustmentRules(out _adjustmentRules, dts, typeOfLocalTime, transitionType, StandardTime, GmtTime);
- }
-
- ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
- }
-
- // ---- SECTION: public methods --------------*
- //
- // GetAdjustmentRules -
- //
- // returns a cloned array of AdjustmentRule objects
- //
- public AdjustmentRule[] GetAdjustmentRules()
- {
- if (_adjustmentRules == null)
- {
- return Array.Empty<AdjustmentRule>();
- }
-
- // The rules we use in Unix cares mostly about the start and end dates but doesn't fill the transition start and end info.
- // as the rules now is public, we should fill it properly so the caller doesn't have to know how we use it internally
- // and can use it as it is used in Windows
-
- AdjustmentRule[] rules = new AdjustmentRule[_adjustmentRules.Length];
-
- for (int i = 0; i < _adjustmentRules.Length; i++)
- {
- var rule = _adjustmentRules[i];
- var start = rule.DateStart.Kind == DateTimeKind.Utc ?
- new DateTime(TimeZoneInfo.ConvertTime(rule.DateStart, this).Ticks, DateTimeKind.Unspecified) :
- rule.DateStart;
- var end = rule.DateEnd.Kind == DateTimeKind.Utc ?
- new DateTime(TimeZoneInfo.ConvertTime(rule.DateEnd, this).Ticks - 1, DateTimeKind.Unspecified) :
- rule.DateEnd;
-
- var startTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, start.Hour, start.Minute, start.Second), start.Month, start.Day);
- var endTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, end.Hour, end.Minute, end.Second), end.Month, end.Day);
-
- rules[i] = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(start.Date, end.Date, rule.DaylightDelta, startTransition, endTransition);
- }
-
- return rules;
- }
-
- public static TimeZoneInfo FindSystemTimeZoneById(string id)
- {
- // UNIXTODO
- throw new NotImplementedException();
- }
-
- private static void PopulateAllSystemTimeZones(CachedData cachedData)
- {
- Debug.Assert(Monitor.IsEntered(cachedData));
- // UNIXTODO
- throw new NotImplementedException();
- }
-
- internal static Byte[] GetLocalTzFile()
- {
- // UNIXTODO
- throw new NotImplementedException();
- }
-
- private static TimeZoneInfo GetLocalTimeZone(CachedData cachedData)
- {
- Byte[] rawData = GetLocalTzFile();
-
- if (rawData != null)
- {
- try
- {
- return new TimeZoneInfo(rawData, false); // create a TimeZoneInfo instance from the TZif data w/ DST support
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
- try
- {
- return new TimeZoneInfo(rawData, true); // create a TimeZoneInfo instance from the TZif data w/o DST support
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
- }
- // the data returned from the PAL is completely bogus; return a dummy entry
- return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
- }
-
- // TZFILE(5) BSD File Formats Manual TZFILE(5)
- //
- // NAME
- // tzfile -- timezone information
- //
- // SYNOPSIS
- // #include "/usr/src/lib/libc/stdtime/tzfile.h"
- //
- // DESCRIPTION
- // The time zone information files used by tzset(3) begin with the magic
- // characters ``TZif'' to identify them as time zone information files, fol-
- // lowed by sixteen bytes reserved for future use, followed by four four-
- // byte values written in a ``standard'' byte order (the high-order byte of
- // the value is written first). These values are, in order:
- //
- // tzh_ttisgmtcnt The number of UTC/local indicators stored in the file.
- // tzh_ttisstdcnt The number of standard/wall indicators stored in the
- // file.
- // tzh_leapcnt The number of leap seconds for which data is stored in
- // the file.
- // tzh_timecnt The number of ``transition times'' for which data is
- // stored in the file.
- // tzh_typecnt The number of ``local time types'' for which data is
- // stored in the file (must not be zero).
- // tzh_charcnt The number of characters of ``time zone abbreviation
- // strings'' stored in the file.
- //
- // The above header is followed by tzh_timecnt four-byte values of type
- // long, sorted in ascending order. These values are written in ``stan-
- // dard'' byte order. Each is used as a transition time (as returned by
- // time(3)) at which the rules for computing local time change. Next come
- // tzh_timecnt one-byte values of type unsigned char; each one tells which
- // of the different types of ``local time'' types described in the file is
- // associated with the same-indexed transition time. These values serve as
- // indices into an array of ttinfo structures that appears next in the file;
- // these structures are defined as follows:
- //
- // struct ttinfo {
- // long tt_gmtoff;
- // int tt_isdst;
- // unsigned int tt_abbrind;
- // };
- //
- // Each structure is written as a four-byte value for tt_gmtoff of type
- // long, in a standard byte order, followed by a one-byte value for tt_isdst
- // and a one-byte value for tt_abbrind. In each structure, tt_gmtoff gives
- // the number of seconds to be added to UTC, tt_isdst tells whether t_isdst
- // should be set by localtime(3) and tt_abbrind serves as an index into the
- // array of time zone abbreviation characters that follow the ttinfo struc-
- // ture(s) in the file.
- //
- // Then there are tzh_leapcnt pairs of four-byte values, written in standard
- // byte order; the first value of each pair gives the time (as returned by
- // time(3)) at which a leap second occurs; the second gives the total number
- // of leap seconds to be applied after the given time. The pairs of values
- // are sorted in ascending order by time.b
- //
- // Then there are tzh_ttisstdcnt standard/wall indicators, each stored as a
- // one-byte value; they tell whether the transition times associated with
- // local time types were specified as standard time or wall clock time, and
- // are used when a time zone file is used in handling POSIX-style time zone
- // environment variables.
- //
- // Finally there are tzh_ttisgmtcnt UTC/local indicators, each stored as a
- // one-byte value; they tell whether the transition times associated with
- // local time types were specified as UTC or local time, and are used when a
- // time zone file is used in handling POSIX-style time zone environment
- // variables.
- //
- // localtime uses the first standard-time ttinfo structure in the file (or
- // simply the first ttinfo structure in the absence of a standard-time
- // structure) if either tzh_timecnt is zero or the time argument is less
- // than the first transition time recorded in the file.
- //
- // SEE ALSO
- // ctime(3), time2posix(3), zic(8)
- //
- // BSD September 13, 1994 BSD
- //
- //
- //
- // TIME(3) BSD Library Functions Manual TIME(3)
- //
- // NAME
- // time -- get time of day
- //
- // LIBRARY
- // Standard C Library (libc, -lc)
- //
- // SYNOPSIS
- // #include <time.h>
- //
- // time_t
- // time(time_t *tloc);
- //
- // DESCRIPTION
- // The time() function returns the value of time in seconds since 0 hours, 0
- // minutes, 0 seconds, January 1, 1970, Coordinated Universal Time, without
- // including leap seconds. If an error occurs, time() returns the value
- // (time_t)-1.
- //
- // The return value is also stored in *tloc, provided that tloc is non-null.
- //
- // ERRORS
- // The time() function may fail for any of the reasons described in
- // gettimeofday(2).
- //
- // SEE ALSO
- // gettimeofday(2), ctime(3)
- //
- // STANDARDS
- // The time function conforms to IEEE Std 1003.1-2001 (``POSIX.1'').
- //
- // BUGS
- // Neither ISO/IEC 9899:1999 (``ISO C99'') nor IEEE Std 1003.1-2001
- // (``POSIX.1'') requires time() to set errno on failure; thus, it is impos-
- // sible for an application to distinguish the valid time value -1 (repre-
- // senting the last UTC second of 1969) from the error return value.
- //
- // Systems conforming to earlier versions of the C and POSIX standards
- // (including older versions of FreeBSD) did not set *tloc in the error
- // case.
- //
- // HISTORY
- // A time() function appeared in Version 6 AT&T UNIX.
- //
- // BSD July 18, 2003 BSD
- //
- //
-
- //
- // TZif_CalculateTransitionTime -
- //
- // Example inputs:
- // -----------------
- // utc = 1918-03-31T10:00:00.0000000Z
- // transitionType = {-08:00:00 DST=False, Index 4}
- // standardTime = False
- // gmtTime = False
- //
- private static TransitionTime TZif_CalculateTransitionTime(DateTime utc, TimeSpan offset,
- TZifType transitionType, Boolean standardTime,
- Boolean gmtTime, out DateTime ruleDate)
- {
- // convert from UTC to local clock time
- Int64 ticks = utc.Ticks + offset.Ticks;
- if (ticks > DateTime.MaxValue.Ticks)
- {
- utc = DateTime.MaxValue;
- }
- else if (ticks < DateTime.MinValue.Ticks)
- {
- utc = DateTime.MinValue;
- }
- else
- {
- utc = new DateTime(ticks);
- }
-
- DateTime timeOfDay = new DateTime(1, 1, 1, utc.Hour, utc.Minute, utc.Second, utc.Millisecond);
- int month = utc.Month;
- int day = utc.Day;
-
- ruleDate = new DateTime(utc.Year, month, day);
- // FUTURE: take standardTime/gmtTime into account
- return TransitionTime.CreateFixedDateRule(timeOfDay, month, day);
- }
-
- private static void TZif_GenerateAdjustmentRules(out AdjustmentRule[] rules, DateTime[] dts, Byte[] typeOfLocalTime,
- TZifType[] transitionType, Boolean[] StandardTime, Boolean[] GmtTime)
- {
- rules = null;
-
- int index = 0;
- List<AdjustmentRule> rulesList = new List<AdjustmentRule>(1);
- bool succeeded = true;
-
- while (succeeded && index < dts.Length)
- {
- succeeded = TZif_GenerateAdjustmentRule(ref index, ref rulesList, dts, typeOfLocalTime, transitionType, StandardTime, GmtTime);
- }
-
- rules = rulesList.ToArray();
- if (rules != null && rules.Length == 0)
- {
- rules = null;
- }
- }
-
-
- private static bool TZif_GenerateAdjustmentRule(ref int startIndex, ref List<AdjustmentRule> rulesList, DateTime[] dts, Byte[] typeOfLocalTime,
- TZifType[] transitionType, Boolean[] StandardTime, Boolean[] GmtTime)
- {
- int index = startIndex;
- bool Dst = false;
- int DstStartIndex = -1;
- int DstEndIndex = -1;
- DateTime startDate = DateTime.MinValue.Date;
- DateTime endDate = DateTime.MaxValue.Date;
-
-
- // find the next DST transition start time index
- while (!Dst && index < typeOfLocalTime.Length)
- {
- int typeIndex = typeOfLocalTime[index];
- if (typeIndex < transitionType.Length && transitionType[typeIndex].IsDst)
- {
- // found the next DST transition start time
- Dst = true;
- DstStartIndex = index;
- }
- else
- {
- index++;
- }
- }
-
- // find the next DST transition end time index
- while (Dst && index < typeOfLocalTime.Length)
- {
- int typeIndex = typeOfLocalTime[index];
- if (typeIndex < transitionType.Length && !transitionType[typeIndex].IsDst)
- {
- // found the next DST transition end time
- Dst = false;
- DstEndIndex = index;
- }
- else
- {
- index++;
- }
- }
-
-
- //
- // construct the adjustment rule from the two indices
- //
- if (DstStartIndex >= 0)
- {
- DateTime startTransitionDate = dts[DstStartIndex];
- DateTime endTransitionDate;
-
-
- if (DstEndIndex == -1)
- {
- // we found a DST start but no DST end; in this case use the
- // prior non-DST entry if it exists, else use the current entry for both start and end (e.g., zero daylightDelta)
- if (DstStartIndex > 0)
- {
- DstEndIndex = DstStartIndex - 1;
- }
- else
- {
- DstEndIndex = DstStartIndex;
- }
- endTransitionDate = DateTime.MaxValue;
- }
- else
- {
- endTransitionDate = dts[DstEndIndex];
- }
-
- int dstStartTypeIndex = typeOfLocalTime[DstStartIndex];
- int dstEndTypeIndex = typeOfLocalTime[DstEndIndex];
-
- TimeSpan daylightBias = transitionType[dstStartTypeIndex].UtcOffset - transitionType[dstEndTypeIndex].UtcOffset;
- // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
- // with DateTimeOffset, SQL Server, and the W3C XML Specification
- if (daylightBias.Ticks % TimeSpan.TicksPerMinute != 0)
- {
- daylightBias = new TimeSpan(daylightBias.Hours, daylightBias.Minutes, 0);
- }
-
- //
- // the normal case is less than 12 months between transition times. However places like America/Catamarca
- // have DST from 1946-1963 straight without a gap. In that case we need to create a series of Adjustment
- // Rules to fudge the multi-year DST period
- //
- if ((endTransitionDate - startTransitionDate).Ticks <= TimeSpan.TicksPerDay * 364)
- {
- TransitionTime dstStart;
- TransitionTime dstEnd;
- TimeSpan startTransitionOffset = (DstStartIndex > 0 ? transitionType[typeOfLocalTime[DstStartIndex - 1]].UtcOffset : transitionType[dstEndTypeIndex].UtcOffset);
- TimeSpan endTransitionOffset = (DstEndIndex > 0 ? transitionType[typeOfLocalTime[DstEndIndex - 1]].UtcOffset : transitionType[dstStartTypeIndex].UtcOffset);
-
-
- dstStart = TZif_CalculateTransitionTime(startTransitionDate,
- startTransitionOffset,
- transitionType[dstStartTypeIndex],
- StandardTime[dstStartTypeIndex],
- GmtTime[dstStartTypeIndex],
- out startDate);
-
- dstEnd = TZif_CalculateTransitionTime(endTransitionDate,
- endTransitionOffset,
- transitionType[dstEndTypeIndex],
- StandardTime[dstEndTypeIndex],
- GmtTime[dstEndTypeIndex],
- out endDate);
-
-
-
- // calculate the AdjustmentRule end date
- if (DstStartIndex >= DstEndIndex)
- {
- // we found a DST start but no DST end
- endDate = DateTime.MaxValue.Date;
- }
-
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(startDate, endDate, daylightBias, dstStart, dstEnd);
- rulesList.Add(r);
- }
- else
- {
- // create the multi-year DST rule series:
- //
- // For example America/Catamarca:
- // 1946-10-01T04:00:00.0000000Z {-03:00:00 DST=True}
- // 1963-10-01T03:00:00.0000000Z {-04:00:00 DST=False}
- //
- // gets converted into a series of overlapping 5/7month adjustment rules:
- //
- // [AdjustmentRule #0] // start rule
- // [1946/09/31 - 1947/06/15] // * starts 1 day prior to startTransitionDate
- // [start: 10/01 @4:00 ] // * N months long, stopping at month 6 or 11
- // [end : 07/15 ] // notice how the _end_ is outside the range
- //
- // [AdjustmentRule #1] // middle-year all-DST rule
- // [1947/06/16 - 1947/11/15] // * starts 1 day after last day in previous rule
- // [start: 05/15 ] // * 5 months long, stopping at month 6 or 11
- // [end : 12/15 ] // notice how the _start and end_ are outside the range
- //
- // [AdjustmentRule #2] // middle-year all-DST rule
- // [1947/11/16 - 1947/06/15] // * starts 1 day after last day in previous rule
- // [start: 10/01 ] // * 7 months long, stopping at month 6 or 11
- // [end : 07/15 ] // notice how the _start and end_ are outside the range
- //
- // .........................
- //
- // [AdjustmentRule #N] // end rule
- // [1963/06/16 - 1946/10/02] // * starts 1 day after last day in previous rule
- // [start: 05/15 ] // * N months long, stopping 1 day after endTransitionDate
- // [end : 10/01 ] // notice how the _start_ is outside the range
- //
-
- // create the first rule from N to either 06/15 or 11/15
- TZif_CreateFirstMultiYearRule(ref rulesList, daylightBias, startTransitionDate, DstStartIndex, dstStartTypeIndex, dstEndTypeIndex,
- dts, transitionType, typeOfLocalTime, StandardTime, GmtTime);
-
- // create the filler rules
- TZif_CreateMiddleMultiYearRules(ref rulesList, daylightBias, endTransitionDate);
-
- // create the last rule
- TZif_CreateLastMultiYearRule(ref rulesList, daylightBias, endTransitionDate, DstStartIndex, dstStartTypeIndex, DstEndIndex, dstEndTypeIndex,
- dts, transitionType, typeOfLocalTime, StandardTime, GmtTime);
- }
-
- startIndex = index + 1;
- return true;
- }
-
- // setup the start values for the next call to TZif_GenerateAdjustmentRule(...)
- startIndex = index + 1;
- return false; // did not create a new AdjustmentRule
- }
-
- private static void TZif_CreateFirstMultiYearRule(ref List<AdjustmentRule> rulesList, TimeSpan daylightBias, DateTime startTransitionDate,
- int DstStartIndex, int dstStartTypeIndex, int dstEndTypeIndex, DateTime[] dts, TZifType[] transitionType,
- Byte[] typeOfLocalTime, bool[] StandardTime, bool[] GmtTime)
- {
- // [AdjustmentRule #0] // start rule
- // [1946/09/31 - 1947/06/15] // * starts 1 day prior to startTransitionDate
- // [start: 10/01 @4:00 ] // * N months long, stopping at month 6 or 11
- // [end : 07/15 ] // notice how the _end_ is outside the range
-
- DateTime startDate;
- DateTime endDate;
- TransitionTime dstStart;
- TransitionTime dstEnd;
-
- TimeSpan startTransitionOffset = (DstStartIndex > 0 ? transitionType[typeOfLocalTime[DstStartIndex - 1]].UtcOffset : transitionType[dstEndTypeIndex].UtcOffset);
-
- dstStart = TZif_CalculateTransitionTime(startTransitionDate,
- startTransitionOffset,
- transitionType[dstStartTypeIndex],
- StandardTime[dstStartTypeIndex],
- GmtTime[dstStartTypeIndex],
- out startDate);
-
- //
- // Choosing the endDate based on the startDate:
- //
- // startTransitionDate.Month -> end
- // 1 4|5 8|9 12
- // [-> 06/15]|[-> 11/15]|[-> 06/15]
- //
- int startDateMonth = startDate.Month;
- int startDateYear = startDate.Year;
-
- if (startDateMonth <= 4)
- {
- endDate = new DateTime(startDateYear, 06, 15);
- dstEnd = s_transition7_15;
- }
- else if (startDateMonth <= 8)
- {
- endDate = new DateTime(startDateYear, 11, 15);
- dstEnd = s_transition12_15;
- }
- else if (startDateYear < 9999)
- {
- endDate = new DateTime(startDateYear + 1, 06, 15);
- dstEnd = s_transition7_15;
- }
- else
- {
- endDate = DateTime.MaxValue;
- dstEnd = s_transition7_15;
- }
-
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(startDate, endDate, daylightBias, dstStart, dstEnd);
- rulesList.Add(r);
- }
-
-
- private static void TZif_CreateLastMultiYearRule(ref List<AdjustmentRule> rulesList, TimeSpan daylightBias, DateTime endTransitionDate,
- int DstStartIndex, int dstStartTypeIndex, int DstEndIndex, int dstEndTypeIndex, DateTime[] dts, TZifType[] transitionType,
- Byte[] typeOfLocalTime, bool[] StandardTime, bool[] GmtTime)
- {
- // [AdjustmentRule #N] // end rule
- // [1963/06/16 - 1946/10/02] // * starts 1 day after last day in previous rule
- // [start: 05/15 ] // * N months long, stopping 1 day after endTransitionDate
- // [end : 10/01 ] // notice how the _start_ is outside the range
-
- DateTime endDate;
- TransitionTime dstEnd;
-
- TimeSpan endTransitionOffset = (DstEndIndex > 0 ? transitionType[typeOfLocalTime[DstEndIndex - 1]].UtcOffset : transitionType[dstStartTypeIndex].UtcOffset);
-
-
- dstEnd = TZif_CalculateTransitionTime(endTransitionDate,
- endTransitionOffset,
- transitionType[dstEndTypeIndex],
- StandardTime[dstEndTypeIndex],
- GmtTime[dstEndTypeIndex],
- out endDate);
-
- if (DstStartIndex >= DstEndIndex)
- {
- // we found a DST start but no DST end
- endDate = DateTime.MaxValue.Date;
- }
-
- AdjustmentRule prevRule = rulesList[rulesList.Count - 1]; // grab the last element of the MultiYearRule sequence
- int y = prevRule.DateEnd.Year;
- if (prevRule.DateEnd.Month <= 6)
- {
- // create a rule from 06/16/YYYY to endDate
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(new DateTime(y, 06, 16), endDate, daylightBias, s_transition5_15, dstEnd);
- rulesList.Add(r);
- }
- else
- {
- // create a rule from 11/16/YYYY to endDate
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(new DateTime(y, 11, 16), endDate, daylightBias, s_transition10_15, dstEnd);
- rulesList.Add(r);
- }
- }
-
-
- private static void TZif_CreateMiddleMultiYearRules(ref List<AdjustmentRule> rulesList, TimeSpan daylightBias, DateTime endTransitionDate)
- {
- //
- // [AdjustmentRule #1] // middle-year all-DST rule
- // [1947/06/16 - 1947/11/15] // * starts 1 day after last day in previous rule
- // [start: 05/15 ] // * 5 months long, stopping at month 6 or 11
- // [end : 12/15 ] // notice how the _start and end_ are outside the range
- //
- // [AdjustmentRule #2] // middle-year all-DST rule
- // [1947/11/16 - 1947/06/15] // * starts 1 day after last day in previous rule
- // [start: 10/01 ] // * 7 months long, stopping at month 6 or 11
- // [end : 07/15 ] // notice how the _start and end_ are outside the range
- //
- // .........................
-
- AdjustmentRule prevRule = rulesList[rulesList.Count - 1]; // grab the first element of the MultiYearRule sequence
- DateTime endDate;
-
- //
- // Choosing the last endDate based on the endTransitionDate
- //
- // endTransitionDate.Month -> end
- // 1 4|5 8|9 12
- // [11/15 <-]|[11/15 <-]|[06/15 <-]
- //
- if (endTransitionDate.Month <= 8)
- {
- // set the end date to 11/15/YYYY-1
- endDate = new DateTime(endTransitionDate.Year - 1, 11, 15);
- }
- else
- {
- // set the end date to 06/15/YYYY
- endDate = new DateTime(endTransitionDate.Year, 06, 15);
- }
-
- while (prevRule.DateEnd < endDate)
- {
- // the last endDate will be on either 06/15 or 11/15
- int y = prevRule.DateEnd.Year;
- if (prevRule.DateEnd.Month <= 6)
- {
- // create a rule from 06/16/YYYY to 11/15/YYYY
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(new DateTime(y, 06, 16), new DateTime(y, 11, 15),
- daylightBias, s_transition5_15, s_transition12_15);
- prevRule = r;
- rulesList.Add(r);
- }
- else
- {
- // create a rule from 11/16/YYYY to 06/15/YYYY+1
- AdjustmentRule r = AdjustmentRule.CreateAdjustmentRule(new DateTime(y, 11, 16), new DateTime(y + 1, 06, 15),
- daylightBias, s_transition10_15, s_transition7_15);
- prevRule = r;
- rulesList.Add(r);
- }
- }
- }
-
-
- // Returns the Substring from zoneAbbreviations starting at index and ending at '\0'
- // zoneAbbreviations is expected to be in the form: "PST\0PDT\0PWT\0\PPT"
- private static String TZif_GetZoneAbbreviation(String zoneAbbreviations, int index)
- {
- int lastIndex = zoneAbbreviations.IndexOf('\0', index);
- if (lastIndex > 0)
- {
- return zoneAbbreviations.Substring(index, lastIndex - index);
- }
- else
- {
- return zoneAbbreviations.Substring(index);
- }
- }
-
- // verify the 'index' is referenced from the typeOfLocalTime byte array.
- //
- private static Boolean TZif_ValidTransitionType(int index, Byte[] typeOfLocalTime)
- {
- Boolean result = false;
-
- if (typeOfLocalTime != null)
- {
- for (int i = 0; !result && i < typeOfLocalTime.Length; i++)
- {
- if (index == typeOfLocalTime[i])
- {
- result = true;
- }
- }
- }
- return result;
- }
-
- // Converts an array of bytes into an int - always using standard byte order (Big Endian)
- // per TZif file standard
- private static int TZif_ToInt32(byte[] value, int startIndex)
- {
- return (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
- }
-
- private static void TZif_ParseRaw(Byte[] data, out TZifHead t, out DateTime[] dts, out Byte[] typeOfLocalTime, out TZifType[] transitionType,
- out String zoneAbbreviations, out Boolean[] StandardTime, out Boolean[] GmtTime)
- {
- // initialize the out parameters in case the TZifHead ctor throws
- dts = null;
- typeOfLocalTime = null;
- transitionType = null;
- zoneAbbreviations = String.Empty;
- StandardTime = null;
- GmtTime = null;
-
- // read in the 44-byte TZ header containing the count/length fields
- //
- t = new TZifHead(data, 0);
- int index = TZifHead.Length;
-
- // initialize the containers for the rest of the TZ data
- dts = new DateTime[t.TimeCount];
- typeOfLocalTime = new Byte[t.TimeCount];
- transitionType = new TZifType[t.TypeCount];
- zoneAbbreviations = String.Empty;
- StandardTime = new Boolean[t.TypeCount];
- GmtTime = new Boolean[t.TypeCount];
-
-
- // read in the 4-byte UTC transition points and convert them to Windows
- //
- for (int i = 0; i < t.TimeCount; i++)
- {
- int unixTime = TZif_ToInt32(data, index);
- dts[i] = TZif_UnixTimeToWindowsTime(unixTime);
- index += 4;
- }
-
- // read in the Type Indices; there is a 1:1 mapping of UTC transition points to Type Indices
- // these indices directly map to the array index in the transitionType array below
- //
- for (int i = 0; i < t.TimeCount; i++)
- {
- typeOfLocalTime[i] = data[index];
- index += 1;
- }
-
- // read in the Type table. Each 6-byte entry represents
- // {UtcOffset, IsDst, AbbreviationIndex}
- //
- // each AbbreviationIndex is a character index into the zoneAbbreviations string below
- //
- for (int i = 0; i < t.TypeCount; i++)
- {
- transitionType[i] = new TZifType(data, index);
- index += 6;
- }
-
- // read in the Abbreviation ASCII string. This string will be in the form:
- // "PST\0PDT\0PWT\0\PPT"
- //
- System.Text.Encoding enc = new System.Text.UTF8Encoding();
- zoneAbbreviations = enc.GetString(data, index, (int)t.CharCount);
- index += (int)t.CharCount;
-
- // skip ahead of the Leap-Seconds Adjustment data. In a future release, consider adding
- // support for Leap-Seconds
- //
- index += (int)(t.LeapCount * 8); // skip the leap second transition times
-
- // read in the Standard Time table. There should be a 1:1 mapping between Type-Index and Standard
- // Time table entries.
- //
- // TRUE = transition time is standard time
- // FALSE = transition time is wall clock time
- // ABSENT = transition time is wall clock time
- //
- for (int i = 0; i < t.IsStdCount && i < t.TypeCount && index < data.Length; i++)
- {
- StandardTime[i] = (data[index++] != 0);
- }
-
- // read in the GMT Time table. There should be a 1:1 mapping between Type-Index and GMT Time table
- // entries.
- //
- // TRUE = transition time is UTC
- // FALSE = transition time is local time
- // ABSENT = transition time is local time
- //
- for (int i = 0; i < t.IsGmtCount && i < t.TypeCount && index < data.Length; i++)
- {
- GmtTime[i] = (data[index++] != 0);
- }
- }
-
-
- // Windows NT time is specified as the number of 100 nanosecond intervals since January 1, 1601.
- // UNIX time is specified as the number of seconds since January 1, 1970. There are 134,774 days
- // (or 11,644,473,600 seconds) between these dates.
- //
- private static DateTime TZif_UnixTimeToWindowsTime(int unixTime)
- {
- // Add 11,644,473,600 and multiply by 10,000,000.
- Int64 ntTime = (((Int64)unixTime) + 11644473600) * 10000000;
- return DateTime.FromFileTimeUtc(ntTime);
- }
-
- private struct TZifType
- {
- private const int c_len = 6;
- public static int Length
- {
- get
- {
- return c_len;
- }
- }
-
- public TimeSpan UtcOffset;
- public Boolean IsDst;
- public Byte AbbreviationIndex;
-
- public TZifType(Byte[] data, Int32 index)
- {
- if (data == null || data.Length < index + c_len)
- {
- throw new ArgumentException(SR.Argument_TimeZoneInfoInvalidTZif, nameof(data));
- }
- UtcOffset = new TimeSpan(0, 0, TZif_ToInt32(data, index + 00));
- IsDst = (data[index + 4] != 0);
- AbbreviationIndex = data[index + 5];
- }
- }
-
- private struct TZifHead
- {
- private const int c_len = 44;
- public static int Length
- {
- get
- {
- return c_len;
- }
- }
-
- public TZifHead(Byte[] data, Int32 index)
- {
- if (data == null || data.Length < c_len)
- {
- throw new ArgumentException("bad data", nameof(data));
- }
-
- Magic = (uint)TZif_ToInt32(data, index + 00);
-
- if (Magic != 0x545A6966)
- {
- // 0x545A6966 = {0x54, 0x5A, 0x69, 0x66} = "TZif"
- throw new ArgumentException(SR.Argument_TimeZoneInfoBadTZif, nameof(data));
- }
-
- // don't use the BitConverter class which parses data
- // based on the Endianess of the machine architecture.
- // this data is expected to always be in "standard byte order",
- // regardless of the machine it is being processed on.
-
- IsGmtCount = (uint)TZif_ToInt32(data, index + 20);
- // skip the 16 byte reserved field
- IsStdCount = (uint)TZif_ToInt32(data, index + 24);
- LeapCount = (uint)TZif_ToInt32(data, index + 28);
- TimeCount = (uint)TZif_ToInt32(data, index + 32);
- TypeCount = (uint)TZif_ToInt32(data, index + 36);
- CharCount = (uint)TZif_ToInt32(data, index + 40);
- }
-
- public UInt32 Magic; // TZ_MAGIC "TZif"
- // public Byte[16] Reserved; // reserved for future use
- public UInt32 IsGmtCount; // number of transition time flags
- public UInt32 IsStdCount; // number of transition time flags
- public UInt32 LeapCount; // number of leap seconds
- public UInt32 TimeCount; // number of transition times
- public UInt32 TypeCount; // number of local time types
- public UInt32 CharCount; // number of abbreviated characters
- }
- }
-}
diff --git a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
deleted file mode 100644
index cbdf98560..000000000
--- a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
+++ /dev/null
@@ -1,1161 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose:
-** This class is used to represent a Dynamic TimeZone. It
-** has methods for converting a DateTime between TimeZones,
-** and for reading TimeZone data from the Windows Registry
-**
-**
-============================================================*/
-
-using Microsoft.Win32;
-using Microsoft.Win32.SafeHandles;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Serialization;
-using System.Runtime.Versioning;
-using System.Security;
-using System.Text;
-using System.Threading;
-
-using TIME_ZONE_INFORMATION = Interop.mincore.TIME_ZONE_INFORMATION;
-using TIME_DYNAMIC_ZONE_INFORMATION = Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION;
-using REGISTRY_TIME_ZONE_INFORMATION = Interop.mincore.REGISTRY_TIME_ZONE_INFORMATION;
-
-namespace System
-{
- sealed public partial class TimeZoneInfo
- {
- // registry constants for the 'Time Zones' hive
- //
- private const string c_timeZonesRegistryHive = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones";
- private const string c_timeZonesRegistryHivePermissionList = @"HKEY_LOCAL_MACHINE\" + c_timeZonesRegistryHive;
- private const string c_displayValue = "Display";
- private const string c_daylightValue = "Dlt";
- private const string c_standardValue = "Std";
- private const string c_muiDisplayValue = "MUI_Display";
- private const string c_muiDaylightValue = "MUI_Dlt";
- private const string c_muiStandardValue = "MUI_Std";
- private const string c_timeZoneInfoValue = "TZI";
- private const string c_firstEntryValue = "FirstEntry";
- private const string c_lastEntryValue = "LastEntry";
-
- private const int c_maxKeyLength = 255;
-
- private const int c_regByteLength = 44;
-
- // Number of 100ns ticks per time unit
- private const long c_ticksPerMillisecond = 10000;
- private const long c_ticksPerSecond = c_ticksPerMillisecond * 1000;
- private const long c_ticksPerMinute = c_ticksPerSecond * 60;
- private const long c_ticksPerHour = c_ticksPerMinute * 60;
- private const long c_ticksPerDay = c_ticksPerHour * 24;
- private const long c_ticksPerDayRange = c_ticksPerDay - c_ticksPerMillisecond;
-
- // ---- SECTION: public methods --------------*
-
- //
- // GetAdjustmentRules -
- //
- // returns a cloned array of AdjustmentRule objects
- //
- public AdjustmentRule[] GetAdjustmentRules()
- {
- if (_adjustmentRules == null)
- {
- return Array.Empty<AdjustmentRule>();
- }
-
- return (AdjustmentRule[])_adjustmentRules.Clone();
- }
-
- private static void PopulateAllSystemTimeZones(CachedData cachedData)
- {
- Debug.Assert(Monitor.IsEntered(cachedData));
-
- using (RegistryKey reg = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_timeZonesRegistryHive, writable: false))
- {
- if (reg != null)
- {
- foreach (string keyName in reg.GetSubKeyNames())
- {
- TimeZoneInfo value;
- Exception ex;
- TryGetTimeZone(keyName, false, out value, out ex, cachedData); // populate the cache
- }
- }
- }
- }
-
- // -------- SECTION: constructors -----------------*
- //
- // TimeZoneInfo -
- //
- // private ctor
- //
- private unsafe TimeZoneInfo(TIME_ZONE_INFORMATION zone, Boolean dstDisabled)
- {
- if (String.IsNullOrEmpty(new String(zone.StandardName)))
- {
- _id = c_localId; // the ID must contain at least 1 character - initialize _id to "Local"
- }
- else
- {
- _id = new String(zone.StandardName);
- }
- _baseUtcOffset = new TimeSpan(0, -(zone.Bias), 0);
-
- if (!dstDisabled)
- {
- // only create the adjustment rule if DST is enabled
- REGISTRY_TIME_ZONE_INFORMATION regZone = new REGISTRY_TIME_ZONE_INFORMATION(zone);
- AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(regZone, DateTime.MinValue.Date, DateTime.MaxValue.Date, zone.Bias);
- if (rule != null)
- {
- _adjustmentRules = new AdjustmentRule[1];
- _adjustmentRules[0] = rule;
- }
- }
-
- ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
- _displayName = new String(zone.StandardName);
- _standardDisplayName = new String(zone.StandardName);
- _daylightDisplayName = new String(zone.DaylightName);
- }
-
- // ----- SECTION: internal static utility methods ----------------*
-
- //
- // CheckDaylightSavingTimeNotSupported -
- //
- // Helper function to check if the current TimeZoneInformation struct does not support DST. This
- // check returns true when the DaylightDate == StandardDate
- //
- // This check is only meant to be used for "Local".
- //
- private static Boolean CheckDaylightSavingTimeNotSupported(TIME_ZONE_INFORMATION timeZone)
- {
- return (timeZone.DaylightDate.wYear == timeZone.StandardDate.wYear
- && timeZone.DaylightDate.wMonth == timeZone.StandardDate.wMonth
- && timeZone.DaylightDate.wDayOfWeek == timeZone.StandardDate.wDayOfWeek
- && timeZone.DaylightDate.wDay == timeZone.StandardDate.wDay
- && timeZone.DaylightDate.wHour == timeZone.StandardDate.wHour
- && timeZone.DaylightDate.wMinute == timeZone.StandardDate.wMinute
- && timeZone.DaylightDate.wSecond == timeZone.StandardDate.wSecond
- && timeZone.DaylightDate.wMilliseconds == timeZone.StandardDate.wMilliseconds);
- }
-
- //
- // CreateAdjustmentRuleFromTimeZoneInformation-
- //
- // Converts a REGISTRY_TIME_ZONE_INFORMATION (REG_TZI_FORMAT struct) to an AdjustmentRule
- //
- private static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(REGISTRY_TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
- {
- AdjustmentRule rule;
- bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- if (timeZoneInformation.Bias == defaultBaseUtcOffset)
- {
- // this rule will not contain any information to be used to adjust dates. just ignore it
- return null;
- }
-
- return rule = AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- TimeSpan.Zero, // no daylight saving transition
- TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
- TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0)); // Bias delta is all what we need from this rule
- }
-
- //
- // Create an AdjustmentRule with TransitionTime objects
- //
- TransitionTime daylightTransitionStart;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */))
- {
- return null;
- }
-
- TransitionTime daylightTransitionEnd;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */))
- {
- return null;
- }
-
- if (daylightTransitionStart.Equals(daylightTransitionEnd))
- {
- // this happens when the time zone does support DST but the OS has DST disabled
- return null;
- }
-
- rule = AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- new TimeSpan(0, -timeZoneInformation.DaylightBias, 0),
- (TransitionTime)daylightTransitionStart,
- (TransitionTime)daylightTransitionEnd,
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0));
-
- return rule;
- }
-
- //
- // FindIdFromTimeZoneInformation -
- //
- // Helper function that searches the registry for a time zone entry
- // that matches the TimeZoneInformation struct
- //
- private static String FindIdFromTimeZoneInformation(TIME_ZONE_INFORMATION timeZone, out Boolean dstDisabled)
- {
- dstDisabled = false;
-
- using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(
- c_timeZonesRegistryHive,
- false
- ))
- {
- if (key == null)
- {
- return null;
- }
- foreach (string keyName in key.GetSubKeyNames())
- {
- if (TryCompareTimeZoneInformationToRegistry(timeZone, keyName, out dstDisabled))
- {
- return keyName;
- }
- }
- }
- return null;
- }
-
- //
- // GetLocalTimeZone -
- //
- // Helper function for retrieving the local system time zone.
- //
- // returns a new TimeZoneInfo instance
- //
- // may throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException
- //
- // assumes cachedData lock is taken
- //
- static unsafe private TimeZoneInfo GetLocalTimeZone(CachedData cachedData)
- {
- String id = null;
-
- //
- // Try using the "kernel32!GetDynamicTimeZoneInformation" API to get the "id"
- //
- Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION dynamicTimeZoneInformation =
- new Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION();
-
- // call kernel32!GetDynamicTimeZoneInformation...
- long result = Interop.mincore.GetDynamicTimeZoneInformation(out dynamicTimeZoneInformation);
- if (result == Interop.mincore.TIME_ZONE_ID_INVALID)
- {
- // return a dummy entry
- return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
- }
-
- TIME_ZONE_INFORMATION timeZoneInformation =
- new TIME_ZONE_INFORMATION(dynamicTimeZoneInformation);
-
- Boolean dstDisabled = dynamicTimeZoneInformation.DynamicDaylightTimeDisabled != 0;
-
- // check to see if we can use the key name returned from the API call
- if (!String.IsNullOrEmpty(new String(dynamicTimeZoneInformation.TimeZoneKeyName)))
- {
- TimeZoneInfo zone;
- Exception ex;
-
- if (TryGetTimeZone(new String(dynamicTimeZoneInformation.TimeZoneKeyName), dstDisabled, out zone, out ex, cachedData) == TimeZoneInfoResult.Success)
- {
- // successfully loaded the time zone from the registry
- return zone;
- }
- }
-
- // the key name was not returned or it pointed to a bogus entry - search for the entry ourselves
- id = FindIdFromTimeZoneInformation(timeZoneInformation, out dstDisabled);
-
- if (id != null)
- {
- TimeZoneInfo zone;
- Exception ex;
- if (TryGetTimeZone(id, dstDisabled, out zone, out ex, cachedData) == TimeZoneInfoResult.Success)
- {
- // successfully loaded the time zone from the registry
- return zone;
- }
- }
-
- // We could not find the data in the registry. Fall back to using
- // the data from the Win32 API
- return GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled);
- }
-
- //
- // GetLocalTimeZoneFromWin32Data -
- //
- // Helper function used by 'GetLocalTimeZone()' - this function wraps a bunch of
- // try/catch logic for handling the TimeZoneInfo private constructor that takes
- // a TIME_ZONE_INFORMATION structure.
- //
- private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(TIME_ZONE_INFORMATION timeZoneInformation, Boolean dstDisabled)
- {
- // first try to create the TimeZoneInfo with the original 'dstDisabled' flag
- try
- {
- return new TimeZoneInfo(timeZoneInformation, dstDisabled);
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
-
- // if 'dstDisabled' was false then try passing in 'true' as a last ditch effort
- if (!dstDisabled)
- {
- try
- {
- return new TimeZoneInfo(timeZoneInformation, true);
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
- }
-
- // the data returned from Windows is completely bogus; return a dummy entry
- return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
- }
-
- //
- // FindSystemTimeZoneById -
- //
- // Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
- // This function wraps the logic necessary to keep the private
- // SystemTimeZones cache in working order
- //
- // This function will either return a valid TimeZoneInfo instance or
- // it will throw 'InvalidTimeZoneException' / 'TimeZoneNotFoundException'.
- //
- public static TimeZoneInfo FindSystemTimeZoneById(string id)
- {
- // Special case for Utc as it will not exist in the dictionary with the rest
- // of the system time zones. There is no need to do this check for Local.Id
- // since Local is a real time zone that exists in the dictionary cache
- if (String.Compare(id, c_utcId, StringComparison.OrdinalIgnoreCase) == 0)
- {
- return TimeZoneInfo.Utc;
- }
-
- if (id == null)
- {
- throw new ArgumentNullException(nameof(id));
- }
- if (id.Length == 0 || id.Length > c_maxKeyLength || id.Contains("\0"))
- {
- throw new TimeZoneNotFoundException(String.Format(SR.TimeZoneNotFound_MissingRegistryData, id));
- }
-
- TimeZoneInfo value;
- Exception e;
-
- TimeZoneInfoResult result;
-
- CachedData cachedData = s_cachedData;
-
- lock (cachedData)
- {
- result = TryGetTimeZone(id, false, out value, out e, cachedData);
- }
-
- if (result == TimeZoneInfoResult.Success)
- {
- return value;
- }
- else if (result == TimeZoneInfoResult.InvalidTimeZoneException)
- {
- throw new InvalidTimeZoneException(String.Format(SR.InvalidTimeZone_InvalidRegistryData, id), e);
- }
- else if (result == TimeZoneInfoResult.SecurityException)
- {
- throw new SecurityException(String.Format(SR.Security_CannotReadRegistryData, id), e);
- }
- else
- {
- throw new TimeZoneNotFoundException(String.Format(SR.TimeZoneNotFound_MissingRegistryData, id), e);
- }
- }
-
- //
- // TransitionTimeFromTimeZoneInformation -
- //
- // Converts a REGISTRY_TIME_ZONE_INFORMATION (REG_TZI_FORMAT struct) to a TransitionTime
- //
- // * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
- // * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
- //
- private static bool TransitionTimeFromTimeZoneInformation(REGISTRY_TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
- {
- //
- // SYSTEMTIME -
- //
- // If the time zone does not support daylight saving time or if the caller needs
- // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
- // must be zero. If this date is specified, the DaylightDate value in the
- // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system
- // assumes the time zone data is invalid and no changes will be applied.
- //
- bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- transitionTime = default(TransitionTime);
- return false;
- }
-
- //
- // SYSTEMTIME -
- //
- // * FixedDateRule -
- // If the Year member is not zero, the transition date is absolute; it will only occur one time
- //
- // * FloatingDateRule -
- // To select the correct day in the month, set the Year member to zero, the Hour and Minute
- // members to the transition time, the DayOfWeek member to the appropriate weekday, and the
- // Day member to indicate the occurence of the day of the week within the month (first through fifth).
- //
- // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows:
- // Hour = 2,
- // Month = 4,
- // DayOfWeek = 0,
- // Day = 1.
- //
- // Specify 2:00a.m. on the last Thursday in October as follows:
- // Hour = 2,
- // Month = 10,
- // DayOfWeek = 4,
- // Day = 5.
- //
- if (readStartDate)
- {
- //
- // read the "daylightTransitionStart"
- //
- if (timeZoneInformation.DaylightDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.DaylightDate.wHour,
- timeZoneInformation.DaylightDate.wMinute,
- timeZoneInformation.DaylightDate.wSecond,
- timeZoneInformation.DaylightDate.wMilliseconds),
- timeZoneInformation.DaylightDate.wMonth,
- timeZoneInformation.DaylightDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.DaylightDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.DaylightDate.wHour,
- timeZoneInformation.DaylightDate.wMinute,
- timeZoneInformation.DaylightDate.wSecond,
- timeZoneInformation.DaylightDate.wMilliseconds),
- timeZoneInformation.DaylightDate.wMonth,
- timeZoneInformation.DaylightDate.wDay);
- }
- }
- else
- {
- //
- // read the "daylightTransitionEnd"
- //
- if (timeZoneInformation.StandardDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.StandardDate.wHour,
- timeZoneInformation.StandardDate.wMinute,
- timeZoneInformation.StandardDate.wSecond,
- timeZoneInformation.StandardDate.wMilliseconds),
- timeZoneInformation.StandardDate.wMonth,
- timeZoneInformation.StandardDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.StandardDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.StandardDate.wHour,
- timeZoneInformation.StandardDate.wMinute,
- timeZoneInformation.StandardDate.wSecond,
- timeZoneInformation.StandardDate.wMilliseconds),
- timeZoneInformation.StandardDate.wMonth,
- timeZoneInformation.StandardDate.wDay);
- }
- }
-
- return true;
- }
-
- //
- // TryCreateAdjustmentRules -
- //
- // Helper function that takes
- // 1. a string representing a <time_zone_name> registry key name
- // 2. a RegistryTimeZoneInformation struct containing the default rule
- // 3. an AdjustmentRule[] out-parameter
- //
- // returns
- // TimeZoneInfoResult.InvalidTimeZoneException,
- // TimeZoneInfoResult.TimeZoneNotFoundException,
- // TimeZoneInfoResult.Success
- //
- // Optional, Dynamic Time Zone Registry Data
- // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- //
- // HKLM
- // Software
- // Microsoft
- // Windows NT
- // CurrentVersion
- // Time Zones
- // <time_zone_name>
- // Dynamic DST
- // * "FirstEntry" REG_DWORD "1980"
- // First year in the table. If the current year is less than this value,
- // this entry will be used for DST boundaries
- // * "LastEntry" REG_DWORD "2038"
- // Last year in the table. If the current year is greater than this value,
- // this entry will be used for DST boundaries"
- // * "<year1>" REG_BINARY REG_TZI_FORMAT
- // See REGISTRY_TIME_ZONE_INFORMATION
- // * "<year2>" REG_BINARY REG_TZI_FORMAT
- // See REGISTRY_TIME_ZONE_INFORMATION
- // * "<year3>" REG_BINARY REG_TZI_FORMAT
- // See REGISTRY_TIME_ZONE_INFORMATION
- //
- // This method expects that its caller has already Asserted RegistryPermission.Read
- //
- private static bool TryCreateAdjustmentRules(string id, REGISTRY_TIME_ZONE_INFORMATION defaultTimeZoneInformation, out AdjustmentRule[] rules, out Exception e, int defaultBaseUtcOffset)
- {
- e = null;
-
- try
- {
- using (RegistryKey dynamicKey = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(
- c_timeZonesRegistryHive + "\\" + id + "\\Dynamic DST",
- false
- ))
- {
- if (dynamicKey == null)
- {
- AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(
- defaultTimeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
-
- if (rule == null)
- {
- rules = null;
- }
- else
- {
- rules = new AdjustmentRule[1];
- rules[0] = rule;
- }
-
- return true;
- }
-
- //
- // loop over all of the "<time_zone_name>\Dynamic DST" hive entries
- //
- // read FirstEntry {MinValue - (year1, 12, 31)}
- // read MiddleEntry {(yearN, 1, 1) - (yearN, 12, 31)}
- // read LastEntry {(yearN, 1, 1) - MaxValue }
-
- // read the FirstEntry and LastEntry key values (ex: "1980", "2038")
- Int32 first = (Int32)dynamicKey.GetValue(c_firstEntryValue, -1, RegistryValueOptions.None);
- Int32 last = (Int32)dynamicKey.GetValue(c_lastEntryValue, -1, RegistryValueOptions.None);
-
- if (first == -1 || last == -1 || first > last)
- {
- rules = null;
- return false;
- }
-
- // read the first year entry
- REGISTRY_TIME_ZONE_INFORMATION dtzi;
- Byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[];
- if (regValue == null || regValue.Length != c_regByteLength)
- {
- rules = null;
- return false;
- }
- dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue);
-
- if (first == last)
- {
- // there is just 1 dynamic rule for this time zone.
- AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(dtzi, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
-
- if (rule == null)
- {
- rules = null;
- }
- else
- {
- rules = new AdjustmentRule[1];
- rules[0] = rule;
- }
-
- return true;
- }
-
- List<AdjustmentRule> rulesList = new List<AdjustmentRule>(1);
-
- // there are more than 1 dynamic rules for this time zone.
- AdjustmentRule firstRule = CreateAdjustmentRuleFromTimeZoneInformation(
- dtzi,
- DateTime.MinValue.Date, // MinValue
- new DateTime(first, 12, 31), // December 31, <FirstYear>
- defaultBaseUtcOffset);
- if (firstRule != null)
- {
- rulesList.Add(firstRule);
- }
-
- // read the middle year entries
- for (Int32 i = first + 1; i < last; i++)
- {
- regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[];
- if (regValue == null || regValue.Length != c_regByteLength)
- {
- rules = null;
- return false;
- }
- dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue);
- AdjustmentRule middleRule = CreateAdjustmentRuleFromTimeZoneInformation(
- dtzi,
- new DateTime(i, 1, 1), // January 01, <Year>
- new DateTime(i, 12, 31), // December 31, <Year>
- defaultBaseUtcOffset);
- if (middleRule != null)
- {
- rulesList.Add(middleRule);
- }
- }
- // read the last year entry
- regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[];
- dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue);
- if (regValue == null || regValue.Length != c_regByteLength)
- {
- rules = null;
- return false;
- }
- AdjustmentRule lastRule = CreateAdjustmentRuleFromTimeZoneInformation(
- dtzi,
- new DateTime(last, 1, 1), // January 01, <LastYear>
- DateTime.MaxValue.Date, // MaxValue
- defaultBaseUtcOffset);
- if (lastRule != null)
- {
- rulesList.Add(lastRule);
- }
-
- // convert the ArrayList to an AdjustmentRule array
- rules = rulesList.ToArray();
- if (rules != null && rules.Length == 0)
- {
- rules = null;
- }
- } // end of: using (RegistryKey dynamicKey...
- }
- catch (InvalidCastException ex)
- {
- // one of the RegistryKey.GetValue calls could not be cast to an expected value type
- rules = null;
- e = ex;
- return false;
- }
- catch (ArgumentOutOfRangeException ex)
- {
- rules = null;
- e = ex;
- return false;
- }
- catch (ArgumentException ex)
- {
- rules = null;
- e = ex;
- return false;
- }
- return true;
- }
-
- //
- // TryCompareStandardDate -
- //
- // Helper function that compares the StandardBias and StandardDate portion a
- // TimeZoneInformation struct to a time zone registry entry
- //
- private static Boolean TryCompareStandardDate(TIME_ZONE_INFORMATION timeZone, REGISTRY_TIME_ZONE_INFORMATION registryTimeZoneInfo)
- {
- return timeZone.Bias == registryTimeZoneInfo.Bias
- && timeZone.StandardBias == registryTimeZoneInfo.StandardBias
- && timeZone.StandardDate.wYear == registryTimeZoneInfo.StandardDate.wYear
- && timeZone.StandardDate.wMonth == registryTimeZoneInfo.StandardDate.wMonth
- && timeZone.StandardDate.wDayOfWeek == registryTimeZoneInfo.StandardDate.wDayOfWeek
- && timeZone.StandardDate.wDay == registryTimeZoneInfo.StandardDate.wDay
- && timeZone.StandardDate.wHour == registryTimeZoneInfo.StandardDate.wHour
- && timeZone.StandardDate.wMinute == registryTimeZoneInfo.StandardDate.wMinute
- && timeZone.StandardDate.wSecond == registryTimeZoneInfo.StandardDate.wSecond
- && timeZone.StandardDate.wMilliseconds == registryTimeZoneInfo.StandardDate.wMilliseconds;
- }
-
- //
- // TryCompareTimeZoneInformationToRegistry -
- //
- // Helper function that compares a TimeZoneInformation struct to a time zone registry entry
- //
- static unsafe private Boolean TryCompareTimeZoneInformationToRegistry(TIME_ZONE_INFORMATION timeZone, string id, out Boolean dstDisabled)
- {
- dstDisabled = false;
- using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(
- c_timeZonesRegistryHive + "\\" + id,
- false
- ))
- {
- if (key == null)
- {
- return false;
- }
-
- REGISTRY_TIME_ZONE_INFORMATION registryTimeZoneInfo;
- Byte[] regValue = (Byte[])key.GetValue(c_timeZoneInfoValue, null, RegistryValueOptions.None) as Byte[];
- if (regValue == null || regValue.Length != c_regByteLength) return false;
- registryTimeZoneInfo = new REGISTRY_TIME_ZONE_INFORMATION(regValue);
-
- //
- // first compare the bias and standard date information between the data from the Win32 API
- // and the data from the registry...
- //
- Boolean result = TryCompareStandardDate(timeZone, registryTimeZoneInfo);
-
- if (!result)
- {
- return false;
- }
-
- result = dstDisabled || CheckDaylightSavingTimeNotSupported(timeZone)
- //
- // since Daylight Saving Time is not "disabled", do a straight comparision between
- // the Win32 API data and the registry data ...
- //
- || (timeZone.DaylightBias == registryTimeZoneInfo.DaylightBias
- && timeZone.DaylightDate.wYear == registryTimeZoneInfo.DaylightDate.wYear
- && timeZone.DaylightDate.wMonth == registryTimeZoneInfo.DaylightDate.wMonth
- && timeZone.DaylightDate.wDayOfWeek == registryTimeZoneInfo.DaylightDate.wDayOfWeek
- && timeZone.DaylightDate.wDay == registryTimeZoneInfo.DaylightDate.wDay
- && timeZone.DaylightDate.wHour == registryTimeZoneInfo.DaylightDate.wHour
- && timeZone.DaylightDate.wMinute == registryTimeZoneInfo.DaylightDate.wMinute
- && timeZone.DaylightDate.wSecond == registryTimeZoneInfo.DaylightDate.wSecond
- && timeZone.DaylightDate.wMilliseconds == registryTimeZoneInfo.DaylightDate.wMilliseconds);
-
- // Finally compare the "StandardName" string value...
- //
- // we do not compare "DaylightName" as this TimeZoneInformation field may contain
- // either "StandardName" or "DaylightName" depending on the time of year and current machine settings
- //
- if (result)
- {
- String registryStandardName = key.GetValue(c_standardValue, String.Empty, RegistryValueOptions.None) as String;
- result = String.Compare(registryStandardName, new String(timeZone.StandardName), StringComparison.Ordinal) == 0;
- }
- return result;
- }
- }
-
- //
- // TryGetLocalizedNameByMuiNativeResource -
- //
- // Helper function for retrieving a localized string resource via MUI.
- // The function expects a string in the form: "@resource.dll, -123"
- //
- // "resource.dll" is a language-neutral portable executable (LNPE) file in
- // the %windir%\system32 directory. The OS is queried to find the best-fit
- // localized resource file for this LNPE (ex: %windir%\system32\en-us\resource.dll.mui).
- // If a localized resource file exists, we LoadString resource ID "123" and
- // return it to our caller.
- //
- // <SecurityKernel Critical="True" Ring="0">
- // <CallsSuppressUnmanagedCode Name="Interop.mincore.GetFileMUIPath(System.Int32,System.String,System.Text.StringBuilder,System.Int32&,System.Text.StringBuilder,System.Int32&,System.Int64&):System.Boolean" />
- // <ReferencesCritical Name="Method: TryGetLocalizedNameByNativeResource(String, Int32):String" Ring="1" />
- // </SecurityKernel>
- private static string TryGetLocalizedNameByMuiNativeResource(string resource)
- {
- if (String.IsNullOrEmpty(resource))
- {
- return String.Empty;
- }
-
- // parse "@tzres.dll, -100"
- //
- // filePath = "C:\Windows\System32\tzres.dll"
- // resourceId = -100
- //
- string[] resources = resource.Split(',');
- if (resources.Length != 2)
- {
- return String.Empty;
- }
-
- string filePath;
- int resourceId;
-
- // get the path to Windows\System32
- StringBuilder sb = new StringBuilder(Interop.mincore.MAX_PATH);
- int r = Interop.mincore.GetSystemDirectory(sb, Interop.mincore.MAX_PATH);
- string system32 = sb.ToString();
-
- // trim the string "@tzres.dll" => "tzres.dll"
- string tzresDll = resources[0].TrimStart('@');
-
- try
- {
- filePath = system32 + "\\" + tzresDll;
- }
- catch (ArgumentException)
- {
- // there were probably illegal characters in the path
- return String.Empty;
- }
-
- if (!Int32.TryParse(resources[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out resourceId))
- {
- return String.Empty;
- }
- resourceId = -resourceId;
-
-
- try
- {
- StringBuilder fileMuiPath = StringBuilderCache.Acquire(Interop.mincore.MAX_PATH);
- fileMuiPath.Length = Interop.mincore.MAX_PATH;
- int fileMuiPathLength = Interop.mincore.MAX_PATH;
- int languageLength = 0;
- Int64 enumerator = 0;
-
- Boolean succeeded = Interop.mincore.GetFileMUIPath(
- Interop.mincore.MUI_PREFERRED_UI_LANGUAGES,
- filePath, null /* language */, ref languageLength,
- fileMuiPath, ref fileMuiPathLength, ref enumerator);
- if (!succeeded)
- {
- StringBuilderCache.Release(fileMuiPath);
- return String.Empty;
- }
- return TryGetLocalizedNameByNativeResource(StringBuilderCache.GetStringAndRelease(fileMuiPath), resourceId);
- }
- catch
- {
- return String.Empty;
- }
- }
-
- //
- // TryGetLocalizedNameByNativeResource -
- //
- // Helper function for retrieving a localized string resource via a native resource DLL.
- // The function expects a string in the form: "C:\Windows\System32\en-us\resource.dll"
- //
- // "resource.dll" is a language-specific resource DLL.
- // If the localized resource DLL exists, LoadString(resource) is returned.
- //
- static unsafe private string TryGetLocalizedNameByNativeResource(string filePath, int resource)
- {
- using (SafeLibraryHandle handle =
- Interop.Kernel32.LoadLibraryEx(filePath, IntPtr.Zero, Interop.Kernel32.LOAD_LIBRARY_AS_DATAFILE))
- {
- if (!handle.IsInvalid)
- {
- const int LoadStringMaxLength = 500;
-
- StringBuilder localizedResource = new StringBuilder(LoadStringMaxLength);
-
- int result = Interop.User32.LoadString(handle, resource,
- localizedResource, LoadStringMaxLength);
-
- if (result != 0)
- {
- return localizedResource.ToString();
- }
- }
- }
-
- return String.Empty;
- }
-
- //
- // TryGetLocalizedNamesByRegistryKey -
- //
- // Helper function for retrieving the DisplayName, StandardName, and DaylightName from the registry
- //
- // The function first checks the MUI_ key-values, and if they exist, it loads the strings from the MUI
- // resource dll(s). When the keys do not exist, the function falls back to reading from the standard
- // key-values
- //
- // This method expects that its caller has already Asserted RegistryPermission.Read
- //
- private static Boolean TryGetLocalizedNamesByRegistryKey(RegistryKey key, out String displayName, out String standardName, out String daylightName)
- {
- displayName = String.Empty;
- standardName = String.Empty;
- daylightName = String.Empty;
-
- // read the MUI_ registry keys
- String displayNameMuiResource = key.GetValue(c_muiDisplayValue, String.Empty, RegistryValueOptions.None) as String;
- String standardNameMuiResource = key.GetValue(c_muiStandardValue, String.Empty, RegistryValueOptions.None) as String;
- String daylightNameMuiResource = key.GetValue(c_muiDaylightValue, String.Empty, RegistryValueOptions.None) as String;
-
- // try to load the strings from the native resource DLL(s)
- if (!String.IsNullOrEmpty(displayNameMuiResource))
- {
- displayName = TryGetLocalizedNameByMuiNativeResource(displayNameMuiResource);
- }
-
- if (!String.IsNullOrEmpty(standardNameMuiResource))
- {
- standardName = TryGetLocalizedNameByMuiNativeResource(standardNameMuiResource);
- }
-
- if (!String.IsNullOrEmpty(daylightNameMuiResource))
- {
- daylightName = TryGetLocalizedNameByMuiNativeResource(daylightNameMuiResource);
- }
-
- // fallback to using the standard registry keys
- if (String.IsNullOrEmpty(displayName))
- {
- displayName = key.GetValue(c_displayValue, String.Empty, RegistryValueOptions.None) as String;
- }
- if (String.IsNullOrEmpty(standardName))
- {
- standardName = key.GetValue(c_standardValue, String.Empty, RegistryValueOptions.None) as String;
- }
- if (String.IsNullOrEmpty(daylightName))
- {
- daylightName = key.GetValue(c_daylightValue, String.Empty, RegistryValueOptions.None) as String;
- }
-
- return true;
- }
-
- //
- // TryGetTimeZoneByRegistryKey -
- //
- // Helper function that takes a string representing a <time_zone_name> registry key name
- // and returns a TimeZoneInfo instance.
- //
- // returns
- // TimeZoneInfoResult.InvalidTimeZoneException,
- // TimeZoneInfoResult.TimeZoneNotFoundException,
- // TimeZoneInfoResult.SecurityException,
- // TimeZoneInfoResult.Success
- //
- //
- // Standard Time Zone Registry Data
- // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- // HKLM
- // Software
- // Microsoft
- // Windows NT
- // CurrentVersion
- // Time Zones
- // <time_zone_name>
- // * STD, REG_SZ "Standard Time Name"
- // (For OS installed zones, this will always be English)
- // * MUI_STD, REG_SZ "@tzres.dll,-1234"
- // Indirect string to localized resource for Standard Time,
- // add "%windir%\system32\" after "@"
- // * DLT, REG_SZ "Daylight Time Name"
- // (For OS installed zones, this will always be English)
- // * MUI_DLT, REG_SZ "@tzres.dll,-1234"
- // Indirect string to localized resource for Daylight Time,
- // add "%windir%\system32\" after "@"
- // * Display, REG_SZ "Display Name like (GMT-8:00) Pacific Time..."
- // * MUI_Display, REG_SZ "@tzres.dll,-1234"
- // Indirect string to localized resource for the Display,
- // add "%windir%\system32\" after "@"
- // * TZI, REG_BINARY REG_TZI_FORMAT
- // See REGISTRY_TIME_ZONE_INFORMATION
- //
- private static TimeZoneInfoResult TryGetTimeZoneByRegistryKey(string id, out TimeZoneInfo value, out Exception e)
- {
- e = null;
-
- using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(
- c_timeZonesRegistryHive + "\\" + id,
- false
- ))
- {
- if (key == null)
- {
- value = null;
- return TimeZoneInfoResult.TimeZoneNotFoundException;
- }
-
- REGISTRY_TIME_ZONE_INFORMATION defaultTimeZoneInformation;
- Byte[] regValue = key.GetValue(c_timeZoneInfoValue, null, RegistryValueOptions.None) as Byte[];
- if (regValue == null || regValue.Length != c_regByteLength)
- {
- // the registry value could not be cast to a byte array
- value = null;
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- defaultTimeZoneInformation = new REGISTRY_TIME_ZONE_INFORMATION(regValue);
-
- AdjustmentRule[] adjustmentRules;
- if (!TryCreateAdjustmentRules(id, defaultTimeZoneInformation, out adjustmentRules, out e, defaultTimeZoneInformation.Bias))
- {
- value = null;
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
-
- string displayName;
- string standardName;
- string daylightName;
-
- if (!TryGetLocalizedNamesByRegistryKey(key, out displayName, out standardName, out daylightName))
- {
- value = null;
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
-
- try
- {
- value = new TimeZoneInfo(
- id,
- new TimeSpan(0, -(defaultTimeZoneInformation.Bias), 0),
- displayName,
- standardName,
- daylightName,
- adjustmentRules,
- false);
-
- return TimeZoneInfoResult.Success;
- }
- catch (ArgumentException ex)
- {
- // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
- value = null;
- e = ex;
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- catch (InvalidTimeZoneException ex)
- {
- // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
- value = null;
- e = ex;
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- }
- }
-
- //
- // TryGetTimeZone -
- //
- // Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
- //
- // This function may return null.
- //
- // assumes cachedData lock is taken
- //
- private static TimeZoneInfoResult TryGetTimeZone(string id, Boolean dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData)
- {
- TimeZoneInfoResult result = TimeZoneInfoResult.Success;
- e = null;
- TimeZoneInfo match = null;
-
- // check the cache
- if (cachedData._systemTimeZones != null)
- {
- if (cachedData._systemTimeZones.TryGetValue(id, out match))
- {
- if (dstDisabled && match._supportsDaylightSavingTime)
- {
- // we found a cache hit but we want a time zone without DST and this one has DST data
- value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
- }
- else
- {
- value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
- match._daylightDisplayName, match._adjustmentRules, false);
- }
- return result;
- }
- }
-
- // fall back to reading from the local machine
- // when the cache is not fully populated
- if (!cachedData._allSystemTimeZonesRead)
- {
- result = TryGetTimeZoneByRegistryKey(id, out match, out e);
- if (result == TimeZoneInfoResult.Success)
- {
- if (cachedData._systemTimeZones == null)
- cachedData._systemTimeZones = new LowLevelDictionaryWithIEnumerable<System.TimeZoneInfo.CachedData.OrdinalIgnoreCaseString, TimeZoneInfo>();
-
- cachedData._systemTimeZones.Add(id, match);
-
- if (dstDisabled && match._supportsDaylightSavingTime)
- {
- // we found a cache hit but we want a time zone without DST and this one has DST data
- value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
- }
- else
- {
- value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
- match._daylightDisplayName, match._adjustmentRules, false);
- }
- }
- else
- {
- value = null;
- }
- }
- else
- {
- result = TimeZoneInfoResult.TimeZoneNotFoundException;
- value = null;
- }
-
- return result;
- }
- } // TimezoneInfo
-} // namespace System
diff --git a/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs b/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs
index faa851c70..4701b3e13 100644
--- a/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs
+++ b/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -32,6 +32,608 @@ namespace System
{
sealed public partial class TimeZoneInfo
{
+#pragma warning disable 0420
+ private sealed partial class CachedData
+ {
+ private static TimeZoneInfo GetCurrentOneYearLocal()
+ {
+ // load the data from the OS
+ TimeZoneInfo match;
+
+ TimeZoneInformation timeZoneInformation;
+ if (!GetTimeZoneInfo(out timeZoneInformation))
+ match = CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId);
+ else
+ match = GetLocalTimeZoneFromWin32Data(timeZoneInformation, false);
+ return match;
+ }
+
+ private volatile OffsetAndRule _oneYearLocalFromUtc;
+
+ public OffsetAndRule GetOneYearLocalFromUtc(int year)
+ {
+ OffsetAndRule oneYearLocFromUtc = _oneYearLocalFromUtc;
+ if (oneYearLocFromUtc == null || oneYearLocFromUtc.Year != year)
+ {
+ TimeZoneInfo currentYear = GetCurrentOneYearLocal();
+ AdjustmentRule rule = currentYear._adjustmentRules == null ? null : currentYear._adjustmentRules[0];
+ oneYearLocFromUtc = new OffsetAndRule(year, currentYear.BaseUtcOffset, rule);
+ _oneYearLocalFromUtc = oneYearLocFromUtc;
+ }
+ return oneYearLocFromUtc;
+ }
+ }
+#pragma warning restore 0420
+
+ private sealed class OffsetAndRule
+ {
+ public readonly int Year;
+ public readonly TimeSpan Offset;
+ public readonly AdjustmentRule Rule;
+
+ public OffsetAndRule(int year, TimeSpan offset, AdjustmentRule rule)
+ {
+ Year = year;
+ Offset = offset;
+ Rule = rule;
+ }
+ }
+
+ private static bool GetTimeZoneInfo(out TimeZoneInformation timeZoneInfo)
+ {
+ TIME_DYNAMIC_ZONE_INFORMATION dtzi;
+ long result = Interop.mincore.GetDynamicTimeZoneInformation(out dtzi);
+ if (result == Interop.mincore.TIME_ZONE_ID_INVALID)
+ {
+ timeZoneInfo = null;
+ return false;
+ }
+
+ timeZoneInfo = new TimeZoneInformation(dtzi);
+
+ return true;
+ }
+
+ private TimeZoneInfo(TimeZoneInformation zone, Boolean dstDisabled)
+ {
+ if (String.IsNullOrEmpty(zone.StandardName))
+ {
+ _id = LocalId; // the ID must contain at least 1 character - initialize m_id to "Local"
+ }
+ else
+ {
+ _id = zone.StandardName;
+ }
+ _baseUtcOffset = new TimeSpan(0, -(zone.Dtzi.Bias), 0);
+
+ if (!dstDisabled)
+ {
+ // only create the adjustment rule if DST is enabled
+ AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(zone, DateTime.MinValue.Date, DateTime.MaxValue.Date, zone.Dtzi.Bias);
+ if (rule != null)
+ {
+ _adjustmentRules = new AdjustmentRule[1];
+ _adjustmentRules[0] = rule;
+ }
+ }
+
+ ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
+ _displayName = zone.StandardName;
+ _standardDisplayName = zone.StandardName;
+ _daylightDisplayName = zone.DaylightName;
+ }
+
+ private sealed class TimeZoneInformation
+ {
+ public string StandardName;
+ public string DaylightName;
+ public string TimeZoneKeyName;
+
+ // we need to keep this one for subsequent interops.
+ public TIME_DYNAMIC_ZONE_INFORMATION Dtzi;
+
+ public unsafe TimeZoneInformation(TIME_DYNAMIC_ZONE_INFORMATION dtzi)
+ {
+ StandardName = new String(dtzi.StandardName);
+ DaylightName = new String(dtzi.DaylightName);
+ TimeZoneKeyName = new String(dtzi.TimeZoneKeyName);
+ Dtzi = dtzi;
+ }
+ }
+
+ //
+ // TryGetTimeZone -
+ //
+ // Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
+ //
+ // This function may return null.
+ //
+ // assumes cachedData lock is taken
+ //
+ private static TimeZoneInfoResult TryGetTimeZone(ref TimeZoneInformation timeZoneInformation, Boolean dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData)
+ {
+ TimeZoneInfoResult result = TimeZoneInfoResult.Success;
+ e = null;
+ TimeZoneInfo match = null;
+
+ // check the cache
+ if (cachedData._systemTimeZones != null)
+ {
+ if (cachedData._systemTimeZones.TryGetValue(timeZoneInformation.TimeZoneKeyName, out match))
+ {
+ if (dstDisabled && match._supportsDaylightSavingTime)
+ {
+ // we found a cache hit but we want a time zone without DST and this one has DST data
+ value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
+ }
+ else
+ {
+ value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
+ match._daylightDisplayName, match._adjustmentRules, false);
+ }
+ return result;
+ }
+ }
+
+ // fall back to reading from the local machine
+ // when the cache is not fully populated
+ result = TryGetFullTimeZoneInformation(timeZoneInformation, out match, out e, timeZoneInformation.Dtzi.Bias);
+
+ if (result == TimeZoneInfoResult.Success)
+ {
+ if (cachedData._systemTimeZones == null)
+ cachedData._systemTimeZones = new Dictionary<string, TimeZoneInfo>();
+
+ cachedData._systemTimeZones.Add(timeZoneInformation.TimeZoneKeyName, match);
+
+ if (dstDisabled && match._supportsDaylightSavingTime)
+ {
+ // we found a cache hit but we want a time zone without DST and this one has DST data
+ value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
+ }
+ else
+ {
+ value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
+ match._daylightDisplayName, match._adjustmentRules, false);
+ }
+ }
+ else
+ {
+ value = null;
+ }
+
+ return result;
+ }
+
+ private static TimeZoneInfoResult TryGetFullTimeZoneInformation(TimeZoneInformation timeZoneInformation, out TimeZoneInfo value, out Exception e, int defaultBaseUtcOffset)
+ {
+ uint firstYear, lastYear;
+ AdjustmentRule rule;
+ AdjustmentRule[] zoneRules = null;
+
+ value = null;
+ e = null;
+
+ //
+ // First get the adjustment rules
+ //
+
+ if (Interop.mincore.GetDynamicTimeZoneInformationEffectiveYears(ref timeZoneInformation.Dtzi, out firstYear, out lastYear) != 0)
+ {
+ rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ zoneRules = new AdjustmentRule[1] { rule };
+ }
+ }
+ else
+ {
+ if (firstYear == lastYear)
+ {
+ // there is just 1 dynamic rule for this time zone.
+ rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ zoneRules = new AdjustmentRule[1] { rule };
+ }
+ }
+ else
+ {
+ TIME_ZONE_INFORMATION tzdi = new TIME_ZONE_INFORMATION();
+ LowLevelList<AdjustmentRule> rules = new LowLevelList<AdjustmentRule>();
+
+ //
+ // First rule
+ //
+
+ if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)firstYear, ref timeZoneInformation.Dtzi, out tzdi))
+ {
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, DateTime.MinValue.Date, new DateTime((int)firstYear, 12, 31), defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ rules.Add(rule);
+ }
+
+ for (uint i = firstYear + 1; i < lastYear; i++)
+ {
+ if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)i, ref timeZoneInformation.Dtzi, out tzdi))
+ {
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)i, 1, 1), new DateTime((int)i, 12, 31), defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ rules.Add(rule);
+ }
+ }
+
+ //
+ // Last rule
+ //
+
+ if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)lastYear, ref timeZoneInformation.Dtzi, out tzdi))
+ {
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset);
+ if (rule != null)
+ {
+ rules.Add(rule);
+ }
+
+ if (rules.Count > 0)
+ {
+ zoneRules = rules.ToArray();
+ }
+ }
+ }
+
+ //
+ // Create TimeZoneInfo object
+ //
+ try
+ {
+ // Note that all names we have are localized names as Windows always return the localized names
+ value = new TimeZoneInfo(
+ timeZoneInformation.TimeZoneKeyName,
+ new TimeSpan(0, -(timeZoneInformation.Dtzi.Bias), 0),
+ timeZoneInformation.StandardName, // we use the display name as the standared names
+ timeZoneInformation.StandardName,
+ timeZoneInformation.DaylightName,
+ zoneRules,
+ false);
+
+ return System.TimeZoneInfo.TimeZoneInfoResult.Success;
+ }
+ catch (ArgumentException ex)
+ {
+ // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
+ value = null;
+ e = ex;
+ return System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ catch (InvalidTimeZoneException ex)
+ {
+ // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
+ value = null;
+ e = ex;
+ return System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ }
+
+ //
+ // CreateAdjustmentRuleFromTimeZoneInformation-
+ //
+ // Converts TimeZoneInformation to an AdjustmentRule
+ //
+ private static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(TimeZoneInformation timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
+ {
+ bool supportsDst = (timeZoneInformation.Dtzi.StandardDate.wMonth != 0);
+
+ if (!supportsDst)
+ {
+ if (timeZoneInformation.Dtzi.Bias == defaultBaseUtcOffset)
+ {
+ // this rule will not contain any information to be used to adjust dates. just ignore it
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ TimeSpan.Zero, // no daylight saving transition
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Dtzi.Bias, 0), // Bias delta is all what we need from this rule
+ noDaylightTransitions: false);
+ }
+
+ //
+ // Create an AdjustmentRule with TransitionTime objects
+ //
+ TransitionTime daylightTransitionStart;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */))
+ {
+ return null;
+ }
+
+ TransitionTime daylightTransitionEnd;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */))
+ {
+ return null;
+ }
+
+ if (daylightTransitionStart.Equals(daylightTransitionEnd))
+ {
+ // this happens when the time zone does support DST but the OS has DST disabled
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ new TimeSpan(0, -timeZoneInformation.Dtzi.DaylightBias, 0),
+ (TransitionTime)daylightTransitionStart,
+ (TransitionTime)daylightTransitionEnd,
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Dtzi.Bias, 0),
+ noDaylightTransitions: false);
+ }
+
+ internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(ref TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
+ {
+ bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
+
+ if (!supportsDst)
+ {
+ if (timeZoneInformation.Bias == defaultBaseUtcOffset)
+ {
+ // this rule will not contain any information to be used to adjust dates. just ignore it
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ TimeSpan.Zero, // no daylight saving transition
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
+ TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0), // Bias delta is all what we need from this rule
+ noDaylightTransitions: false);
+ }
+
+ //
+ // Create an AdjustmentRule with TransitionTime objects
+ //
+ TransitionTime daylightTransitionStart;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */))
+ {
+ return null;
+ }
+
+ TransitionTime daylightTransitionEnd;
+ if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */))
+ {
+ return null;
+ }
+
+ if (daylightTransitionStart.Equals(daylightTransitionEnd))
+ {
+ // this happens when the time zone does support DST but the OS has DST disabled
+ return null;
+ }
+
+ return AdjustmentRule.CreateAdjustmentRule(
+ startDate,
+ endDate,
+ new TimeSpan(0, -timeZoneInformation.DaylightBias, 0),
+ (TransitionTime)daylightTransitionStart,
+ (TransitionTime)daylightTransitionEnd,
+ new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0),
+ noDaylightTransitions: false);
+ }
+
+ private static bool TransitionTimeFromTimeZoneInformation(TimeZoneInformation timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
+ {
+ bool supportsDst = (timeZoneInformation.Dtzi.StandardDate.wMonth != 0);
+
+ if (!supportsDst)
+ {
+ transitionTime = default(TransitionTime);
+ return false;
+ }
+
+ if (readStartDate)
+ {
+ //
+ // read the "daylightTransitionStart"
+ //
+ if (timeZoneInformation.Dtzi.DaylightDate.wYear == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.Dtzi.DaylightDate.wHour,
+ timeZoneInformation.Dtzi.DaylightDate.wMinute,
+ timeZoneInformation.Dtzi.DaylightDate.wSecond,
+ timeZoneInformation.Dtzi.DaylightDate.wMilliseconds),
+ timeZoneInformation.Dtzi.DaylightDate.wMonth,
+ timeZoneInformation.Dtzi.DaylightDate.wDay, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.Dtzi.DaylightDate.wDayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.Dtzi.DaylightDate.wHour,
+ timeZoneInformation.Dtzi.DaylightDate.wMinute,
+ timeZoneInformation.Dtzi.DaylightDate.wSecond,
+ timeZoneInformation.Dtzi.DaylightDate.wMilliseconds),
+ timeZoneInformation.Dtzi.DaylightDate.wMonth,
+ timeZoneInformation.Dtzi.DaylightDate.wDay);
+ }
+ }
+ else
+ {
+ //
+ // read the "daylightTransitionEnd"
+ //
+ if (timeZoneInformation.Dtzi.StandardDate.wYear == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.Dtzi.StandardDate.wHour,
+ timeZoneInformation.Dtzi.StandardDate.wMinute,
+ timeZoneInformation.Dtzi.StandardDate.wSecond,
+ timeZoneInformation.Dtzi.StandardDate.wMilliseconds),
+ timeZoneInformation.Dtzi.StandardDate.wMonth,
+ timeZoneInformation.Dtzi.StandardDate.wDay, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.Dtzi.StandardDate.wDayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.Dtzi.StandardDate.wHour,
+ timeZoneInformation.Dtzi.StandardDate.wMinute,
+ timeZoneInformation.Dtzi.StandardDate.wSecond,
+ timeZoneInformation.Dtzi.StandardDate.wMilliseconds),
+ timeZoneInformation.Dtzi.StandardDate.wMonth,
+ timeZoneInformation.Dtzi.StandardDate.wDay);
+ }
+ }
+
+ return true;
+ }
+
+ //
+ // TransitionTimeFromTimeZoneInformation -
+ //
+ // Converts a TimeZoneInformation (REG_TZI_FORMAT struct) to a TransitionTime
+ //
+ // * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
+ // * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
+ //
+ private static bool TransitionTimeFromTimeZoneInformation(TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
+ {
+ //
+ // SYSTEMTIME -
+ //
+ // If the time zone does not support daylight saving time or if the caller needs
+ // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
+ // must be zero. If this date is specified, the DaylightDate value in the
+ // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system
+ // assumes the time zone data is invalid and no changes will be applied.
+ //
+ bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
+
+ if (!supportsDst)
+ {
+ transitionTime = default(TransitionTime);
+ return false;
+ }
+
+ //
+ // SYSTEMTIME -
+ //
+ // * FixedDateRule -
+ // If the Year member is not zero, the transition date is absolute; it will only occur one time
+ //
+ // * FloatingDateRule -
+ // To select the correct day in the month, set the Year member to zero, the Hour and Minute
+ // members to the transition time, the DayOfWeek member to the appropriate weekday, and the
+ // Day member to indicate the occurence of the day of the week within the month (first through fifth).
+ //
+ // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows:
+ // Hour = 2,
+ // Month = 4,
+ // DayOfWeek = 0,
+ // Day = 1.
+ //
+ // Specify 2:00a.m. on the last Thursday in October as follows:
+ // Hour = 2,
+ // Month = 10,
+ // DayOfWeek = 4,
+ // Day = 5.
+ //
+ if (readStartDate)
+ {
+ //
+ // read the "daylightTransitionStart"
+ //
+ if (timeZoneInformation.DaylightDate.wYear == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.DaylightDate.wHour,
+ timeZoneInformation.DaylightDate.wMinute,
+ timeZoneInformation.DaylightDate.wSecond,
+ timeZoneInformation.DaylightDate.wMilliseconds),
+ timeZoneInformation.DaylightDate.wMonth,
+ timeZoneInformation.DaylightDate.wDay, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.DaylightDate.wDayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.DaylightDate.wHour,
+ timeZoneInformation.DaylightDate.wMinute,
+ timeZoneInformation.DaylightDate.wSecond,
+ timeZoneInformation.DaylightDate.wMilliseconds),
+ timeZoneInformation.DaylightDate.wMonth,
+ timeZoneInformation.DaylightDate.wDay);
+ }
+ }
+ else
+ {
+ //
+ // read the "daylightTransitionEnd"
+ //
+ if (timeZoneInformation.StandardDate.wYear == 0)
+ {
+ transitionTime = TransitionTime.CreateFloatingDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.StandardDate.wHour,
+ timeZoneInformation.StandardDate.wMinute,
+ timeZoneInformation.StandardDate.wSecond,
+ timeZoneInformation.StandardDate.wMilliseconds),
+ timeZoneInformation.StandardDate.wMonth,
+ timeZoneInformation.StandardDate.wDay, /* Week 1-5 */
+ (DayOfWeek)timeZoneInformation.StandardDate.wDayOfWeek);
+ }
+ else
+ {
+ transitionTime = TransitionTime.CreateFixedDateRule(
+ new DateTime(1, /* year */
+ 1, /* month */
+ 1, /* day */
+ timeZoneInformation.StandardDate.wHour,
+ timeZoneInformation.StandardDate.wMinute,
+ timeZoneInformation.StandardDate.wSecond,
+ timeZoneInformation.StandardDate.wMilliseconds),
+ timeZoneInformation.StandardDate.wMonth,
+ timeZoneInformation.StandardDate.wDay);
+ }
+ }
+
+ return true;
+ }
+
// ---- SECTION: public methods --------------*
//
@@ -65,6 +667,36 @@ namespace System
}
}
+ /// <summary>
+ /// Helper function used by 'GetLocalTimeZone()' - this function wraps a bunch of
+ /// try/catch logic for handling the TimeZoneInfo private constructor that takes
+ /// a Win32Native.TimeZoneInformation structure.
+ /// </summary>
+ private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(TimeZoneInformation timeZoneInformation, bool dstDisabled)
+ {
+ // first try to create the TimeZoneInfo with the original 'dstDisabled' flag
+ try
+ {
+ return new TimeZoneInfo(timeZoneInformation, dstDisabled);
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+
+ // if 'dstDisabled' was false then try passing in 'true' as a last ditch effort
+ if (!dstDisabled)
+ {
+ try
+ {
+ return new TimeZoneInfo(timeZoneInformation, dstDisabled: true);
+ }
+ catch (ArgumentException) { }
+ catch (InvalidTimeZoneException) { }
+ }
+
+ // the data returned from Windows is completely bogus; return a dummy entry
+ return CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId);
+ }
+
public static TimeZoneInfo FindSystemTimeZoneById(string id)
{
if (id == null)
@@ -73,7 +705,7 @@ namespace System
}
if (id.Length == 0 || id.Length > 255 || id.Contains("\0"))
{
- throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingRegistryData, id));
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id));
}
//
@@ -108,7 +740,30 @@ namespace System
}
}
}
- throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingRegistryData, id));
+ throw new TimeZoneNotFoundException(SR.Format(SR.TimeZoneNotFound_MissingData, id));
+ }
+
+ // DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
+ internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
+ {
+ bool isDaylightSavings = false;
+ isAmbiguousLocalDst = false;
+ TimeSpan baseOffset;
+ int timeYear = time.Year;
+
+ OffsetAndRule match = s_cachedData.GetOneYearLocalFromUtc(timeYear);
+ baseOffset = match.Offset;
+
+ if (match.Rule != null)
+ {
+ baseOffset = baseOffset + match.Rule.BaseUtcOffsetDelta;
+ if (match.Rule.HasDaylightSaving)
+ {
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
+ baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
+ }
+ }
+ return baseOffset;
}
private static bool EqualStandardDates(TimeZoneInformation timeZone, ref TIME_DYNAMIC_ZONE_INFORMATION tdzi)
@@ -153,7 +808,7 @@ namespace System
//
// enumerate all time zones till find a match and with valid key name
//
- internal static unsafe bool FindMatchToCurrentTimeZone(TimeZoneInformation timeZoneInformation)
+ private static unsafe bool FindMatchToCurrentTimeZone(TimeZoneInformation timeZoneInformation)
{
uint index = 0;
uint result = 0; // ERROR_SUCCESS
@@ -203,7 +858,7 @@ namespace System
TimeZoneInformation timeZoneInformation;
if (!GetTimeZoneInfo(out timeZoneInformation))
{
- return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
+ return CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId);
}
Boolean dstDisabled = timeZoneInformation.Dtzi.DynamicDaylightTimeDisabled != 0;
@@ -223,5 +878,15 @@ namespace System
// Fall back to using the data from the Win32 API
return GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled);
}
- } // TimezoneInfo
-} // namespace System
+
+ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out TimeZoneInfo value, out Exception e)
+ {
+ // This method should be unreachable
+ Debug.Assert(false);
+
+ e = null;
+ value = null;
+ return TimeZoneInfoResult.InvalidTimeZoneException;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs
deleted file mode 100644
index eee72972b..000000000
--- a/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs
+++ /dev/null
@@ -1,3870 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-** Purpose:
-** This class is used to represent a Dynamic TimeZone. It
-** has methods for converting a DateTime between TimeZones.
-**
-**
-============================================================*/
-
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Globalization;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Threading;
-
-using TIME_ZONE_INFORMATION = Interop.mincore.TIME_ZONE_INFORMATION;
-using TIME_DYNAMIC_ZONE_INFORMATION = Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION;
-
-namespace System
-{
- //
- // DateTime uses TimeZoneInfo under the hood for IsDaylightSavingTime, IsAmbiguousTime, and GetUtcOffset.
- // These TimeZoneInfo APIs can throw ArgumentException when an Invalid-Time is passed in. To avoid this
- // unwanted behavior in DateTime public APIs, DateTime internally passes the
- // TimeZoneInfoOptions.NoThrowOnInvalidTime flag to internal TimeZoneInfo APIs.
- //
- // In the future we can consider exposing similar options on the public TimeZoneInfo APIs if there is enough
- // demand for this alternate behavior.
- //
- [Flags]
- internal enum TimeZoneInfoOptions
- {
- None = 1,
- NoThrowOnInvalidTime = 2
- };
-
-
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- sealed public partial class TimeZoneInfo : IEquatable<TimeZoneInfo>, ISerializable, IDeserializationCallback
- {
- // ---- SECTION: members for internal support ---------*
- internal enum TimeZoneInfoResult
- {
- Success = 0,
- TimeZoneNotFoundException = 1,
- InvalidTimeZoneException = 2,
- SecurityException = 3
- };
-
- // ---- SECTION: members supporting exposed properties -------------*
- private readonly String _id;
- private readonly String _displayName;
- private readonly String _standardDisplayName;
- private readonly String _daylightDisplayName;
- private readonly TimeSpan _baseUtcOffset;
- private readonly Boolean _supportsDaylightSavingTime;
- private readonly AdjustmentRule[] _adjustmentRules;
-
- // constants for TimeZoneInfo.Local and TimeZoneInfo.Utc
- private const string c_utcId = "UTC";
- private const string c_localId = "Local";
-
- private static readonly TimeZoneInfo s_utcTimeZone = CreateCustomTimeZone(c_utcId, TimeSpan.Zero, c_utcId, c_utcId);
-
- private static CachedData s_cachedData = new CachedData();
-
- //
- // All cached data are encapsulated in a helper class to allow consistent view even when the data are refreshed using ClearCachedData()
- //
- // For example, TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData. Without the consistent snapshot,
- // there is a chance that the internal ConvertTime calls will throw since 'source' won't be reference equal to the new TimeZoneInfo.Local.
- //
-#pragma warning disable 0420
- private class CachedData
- {
- private volatile TimeZoneInfo _localTimeZone;
-
- private TimeZoneInfo CreateLocal()
- {
- lock (this)
- {
- TimeZoneInfo timeZone = _localTimeZone;
- if (timeZone == null)
- {
- timeZone = TimeZoneInfo.GetLocalTimeZone(this);
-
- // this step is to break the reference equality
- // between TimeZoneInfo.Local and a second time zone
- // such as "Pacific Standard Time"
- timeZone = new TimeZoneInfo(
- timeZone._id,
- timeZone._baseUtcOffset,
- timeZone._displayName,
- timeZone._standardDisplayName,
- timeZone._daylightDisplayName,
- timeZone._adjustmentRules,
- false);
-
- _localTimeZone = timeZone;
- }
- return timeZone;
- }
- }
-
- public TimeZoneInfo Local
- {
- get
- {
- TimeZoneInfo timeZone = _localTimeZone;
- if (timeZone == null)
- {
- timeZone = CreateLocal();
- }
- return timeZone;
- }
- }
-
- //
- // GetCorrespondingKind-
- //
- // Helper function that returns the corresponding DateTimeKind for this TimeZoneInfo
- //
- public DateTimeKind GetCorrespondingKind(TimeZoneInfo timeZone)
- {
- DateTimeKind kind;
-
- //
- // we check reference equality to see if 'this' is the same as
- // TimeZoneInfo.Local or TimeZoneInfo.Utc. This check is needed to
- // support setting the DateTime Kind property to 'Local' or
- // 'Utc' on the ConverTime(...) return value.
- //
- // Using reference equality instead of value equality was a
- // performance based design compromise. The reference equality
- // has much greater performance, but it reduces the number of
- // returned DateTime's that can be properly set as 'Local' or 'Utc'.
- //
- // For example, the user could be converting to the TimeZoneInfo returned
- // by FindSystemTimeZoneById("Pacific Standard Time") and their local
- // machine may be in Pacific time. If we used value equality to determine
- // the corresponding Kind then this conversion would be tagged as 'Local';
- // where as we are currently tagging the returned DateTime as 'Unspecified'
- // in this example. Only when the user passes in TimeZoneInfo.Local or
- // TimeZoneInfo.Utc to the ConvertTime(...) methods will this check succeed.
- //
- if ((object)timeZone == (object)s_utcTimeZone)
- {
- kind = DateTimeKind.Utc;
- }
- else if ((object)timeZone == (object)_localTimeZone)
- {
- kind = DateTimeKind.Local;
- }
- else
- {
- kind = DateTimeKind.Unspecified;
- }
-
- return kind;
- }
-
- public struct OrdinalIgnoreCaseString : IEquatable<OrdinalIgnoreCaseString>
- {
- public static implicit operator string(OrdinalIgnoreCaseString ignoreCaseString)
- {
- return ignoreCaseString._string;
- }
-
- public static implicit operator OrdinalIgnoreCaseString(string input)
- {
- return new OrdinalIgnoreCaseString() { _string = input };
- }
-
- public override int GetHashCode()
- {
- return TextInfo.GetHashCodeOrdinalIgnoreCase(_string);
- }
-
- public bool Equals(OrdinalIgnoreCaseString other)
- {
- if (_string.Length != other._string.Length)
- {
- return false;
- }
- return (String.Compare(_string, other._string, StringComparison.OrdinalIgnoreCase) == 0);
- }
-
- private string _string;
- }
-
- public LowLevelDictionaryWithIEnumerable<OrdinalIgnoreCaseString, TimeZoneInfo> _systemTimeZones;
- public volatile ReadOnlyCollection<TimeZoneInfo> _readOnlySystemTimeZones;
- public bool _allSystemTimeZonesRead;
-
- private static TimeZoneInfo GetCurrentOneYearLocal()
- {
- // load the data from the OS
- TimeZoneInfo match;
-
- TimeZoneInformation timeZoneInformation;
- if (!GetTimeZoneInfo(out timeZoneInformation))
- match = CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
- else
- match = GetLocalTimeZoneFromWin32Data(timeZoneInformation, false);
- return match;
- }
-
- private volatile OffsetAndRule _oneYearLocalFromUtc;
-
- public OffsetAndRule GetOneYearLocalFromUtc(int year)
- {
- OffsetAndRule oneYearLocFromUtc = _oneYearLocalFromUtc;
- if (oneYearLocFromUtc == null || oneYearLocFromUtc.year != year)
- {
- TimeZoneInfo currentYear = GetCurrentOneYearLocal();
- AdjustmentRule rule = currentYear._adjustmentRules == null ? null : currentYear._adjustmentRules[0];
- oneYearLocFromUtc = new OffsetAndRule(year, currentYear.BaseUtcOffset, rule);
- _oneYearLocalFromUtc = oneYearLocFromUtc;
- }
- return oneYearLocFromUtc;
- }
- }
-#pragma warning restore 0420
-
- internal static bool GetTimeZoneInfo(out TimeZoneInformation timeZoneInfo)
- {
-#if PLATFORM_UNIX
- throw new NotImplementedException();
-#else
- TIME_DYNAMIC_ZONE_INFORMATION dtzi = new TIME_DYNAMIC_ZONE_INFORMATION();
- long result = Interop.mincore.GetDynamicTimeZoneInformation(out dtzi);
- if (result == Interop.mincore.TIME_ZONE_ID_INVALID)
- {
- timeZoneInfo = null;
- return false;
- }
-
- timeZoneInfo = new TimeZoneInformation(dtzi);
-
- return true;
-#endif
- }
-
- private class OffsetAndRule
- {
- public int year;
- public TimeSpan offset;
- public AdjustmentRule rule;
- public OffsetAndRule(int year, TimeSpan offset, AdjustmentRule rule)
- {
- this.year = year;
- this.offset = offset;
- this.rule = rule;
- }
- }
-
- // used by GetUtcOffsetFromUtc (DateTime.Now, DateTime.ToLocalTime) for max/min whole-day range checks
- private static DateTime s_maxDateOnly = new DateTime(9999, 12, 31);
- private static DateTime s_minDateOnly = new DateTime(1, 1, 2);
-
- public String Id
- {
- get
- {
- return _id;
- }
- }
-
- public String DisplayName
- {
- get
- {
- return (_displayName == null ? String.Empty : _displayName);
- }
- }
-
- public String StandardName
- {
- get
- {
- return (_standardDisplayName == null ? String.Empty : _standardDisplayName);
- }
- }
-
- public String DaylightName
- {
- get
- {
- return (_daylightDisplayName == null ? String.Empty : _daylightDisplayName);
- }
- }
-
- public TimeSpan BaseUtcOffset
- {
- get
- {
- return _baseUtcOffset;
- }
- }
-
- public Boolean SupportsDaylightSavingTime
- {
- get
- {
- return _supportsDaylightSavingTime;
- }
- }
-
- //
- // GetAmbiguousTimeOffsets -
- //
- // returns an array of TimeSpan objects representing all of
- // possible UTC offset values for this ambiguous time
- //
- public TimeSpan[] GetAmbiguousTimeOffsets(DateTimeOffset dateTimeOffset)
- {
- if (!SupportsDaylightSavingTime)
- {
- throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset));
- }
-
- DateTime adjustedTime = (TimeZoneInfo.ConvertTime(dateTimeOffset, this)).DateTime;
-
- Boolean isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
- if (rule != null && rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
- isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
- }
-
- if (!isAmbiguous)
- {
- throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset));
- }
-
- // the passed in dateTime is ambiguous in this TimeZoneInfo instance
- TimeSpan[] timeSpans = new TimeSpan[2];
- TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta;
-
- // the TimeSpan array must be sorted from least to greatest
- if (rule.DaylightDelta > TimeSpan.Zero)
- {
- timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
- timeSpans[1] = actualUtcOffset + rule.DaylightDelta;
- }
- else
- {
- timeSpans[0] = actualUtcOffset + rule.DaylightDelta;
- timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
- }
- return timeSpans;
- }
-
- public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
- {
- if (!SupportsDaylightSavingTime)
- {
- throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime));
- }
-
- DateTime adjustedTime;
- if (dateTime.Kind == DateTimeKind.Local)
- {
- CachedData cachedData = s_cachedData;
- adjustedTime = TimeZoneInfo.ConvertTime(dateTime, cachedData.Local, this, TimeZoneInfoOptions.None, cachedData);
- }
- else if (dateTime.Kind == DateTimeKind.Utc)
- {
- CachedData cachedData = s_cachedData;
- adjustedTime = TimeZoneInfo.ConvertTime(dateTime, s_utcTimeZone, this, TimeZoneInfoOptions.None, cachedData);
- }
- else
- {
- adjustedTime = dateTime;
- }
-
- Boolean isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
- if (rule != null && rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
- isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
- }
-
- if (!isAmbiguous)
- {
- throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime));
- }
-
- // the passed in dateTime is ambiguous in this TimeZoneInfo instance
- TimeSpan[] timeSpans = new TimeSpan[2];
- TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta;
-
- // the TimeSpan array must be sorted from least to greatest
- if (rule.DaylightDelta > TimeSpan.Zero)
- {
- timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
- timeSpans[1] = actualUtcOffset + rule.DaylightDelta;
- }
- else
- {
- timeSpans[0] = actualUtcOffset + rule.DaylightDelta;
- timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta;
- }
- return timeSpans;
- }
-
- //
- // GetUtcOffset -
- //
- // returns the Universal Coordinated Time (UTC) Offset
- // for the current TimeZoneInfo instance.
- //
- public TimeSpan GetUtcOffset(DateTimeOffset dateTimeOffset)
- {
- return GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this);
- }
-
-
- public TimeSpan GetUtcOffset(DateTime dateTime)
- {
- return GetUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData);
- }
-
- // Shortcut for TimeZoneInfo.Local.GetUtcOffset
- internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
- {
- CachedData cachedData = s_cachedData;
- return cachedData.Local.GetUtcOffset(dateTime, flags, cachedData);
- }
-
- internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
- {
- return GetUtcOffset(dateTime, flags, s_cachedData);
- }
-
- private TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData)
- {
- if (dateTime.Kind == DateTimeKind.Local)
- {
- if (cachedData.GetCorrespondingKind(this) != DateTimeKind.Local)
- {
- //
- // normal case of converting from Local to Utc and then getting the offset from the UTC DateTime
- //
- DateTime adjustedTime = TimeZoneInfo.ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags);
- return GetUtcOffsetFromUtc(adjustedTime, this);
- }
- //
- // Fall through for TimeZoneInfo.Local.GetUtcOffset(date)
- // to handle an edge case with Invalid-Times for DateTime formatting:
- //
- // Consider the invalid PST time "2007-03-11T02:00:00.0000000-08:00"
- //
- // By directly calling GetUtcOffset instead of converting to UTC and then calling GetUtcOffsetFromUtc
- // the correct invalid offset of "-08:00" is returned. In the normal case of converting to UTC as an
- // interim-step, the invalid time is adjusted into a *valid* UTC time which causes a change in output:
- //
- // 1) invalid PST time "2007-03-11T02:00:00.0000000-08:00"
- // 2) converted to UTC "2007-03-11T10:00:00.0000000Z"
- // 3) offset returned "2007-03-11T03:00:00.0000000-07:00"
- //
- }
- else if (dateTime.Kind == DateTimeKind.Utc)
- {
- if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc)
- {
- return _baseUtcOffset;
- }
- else
- {
- //
- // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a
- // special Loss-Less case.
- //
- return GetUtcOffsetFromUtc(dateTime, this);
- }
- }
-
- return GetUtcOffset(dateTime, this, flags);
- }
-
- //
- // IsAmbiguousTime -
- //
- // returns true if the time is during the ambiguous time period
- // for the current TimeZoneInfo instance.
- //
- public Boolean IsAmbiguousTime(DateTimeOffset dateTimeOffset)
- {
- if (!_supportsDaylightSavingTime)
- {
- return false;
- }
-
- DateTimeOffset adjustedTime = TimeZoneInfo.ConvertTime(dateTimeOffset, this);
- return IsAmbiguousTime(adjustedTime.DateTime);
- }
-
-
- public Boolean IsAmbiguousTime(DateTime dateTime)
- {
- return IsAmbiguousTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
- }
-
- internal Boolean IsAmbiguousTime(DateTime dateTime, TimeZoneInfoOptions flags)
- {
- if (!_supportsDaylightSavingTime)
- {
- return false;
- }
-
- DateTime adjustedTime;
- if (dateTime.Kind == DateTimeKind.Local)
- {
- CachedData cachedData = s_cachedData;
- adjustedTime = TimeZoneInfo.ConvertTime(dateTime, cachedData.Local, this, flags, cachedData);
- }
- else if (dateTime.Kind == DateTimeKind.Utc)
- {
- CachedData cachedData = s_cachedData;
- adjustedTime = TimeZoneInfo.ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData);
- }
- else
- {
- adjustedTime = dateTime;
- }
-
-
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
- if (rule != null && rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
- return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
- }
- return false;
- }
-
- //
- // IsDaylightSavingTime -
- //
- // Returns true if the time is during Daylight Saving time
- // for the current TimeZoneInfo instance.
- //
- public Boolean IsDaylightSavingTime(DateTimeOffset dateTimeOffset)
- {
- Boolean isDaylightSavingTime;
- GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this, out isDaylightSavingTime);
- return isDaylightSavingTime;
- }
-
- public Boolean IsDaylightSavingTime(DateTime dateTime)
- {
- return IsDaylightSavingTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData);
- }
-
- internal Boolean IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags)
- {
- return IsDaylightSavingTime(dateTime, flags, s_cachedData);
- }
-
- private Boolean IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData)
- {
- //
- // dateTime.Kind is UTC, then time will be converted from UTC
- // into current instance's timezone
- // dateTime.Kind is Local, then time will be converted from Local
- // into current instance's timezone
- // dateTime.Kind is UnSpecified, then time is already in
- // current instance's timezone
- //
- // Our DateTime handles ambiguous times, (one is in the daylight and
- // one is in standard.) If a new DateTime is constructed during ambiguous
- // time, it is defaulted to "Standard" (i.e. this will return false).
- // For Invalid times, we will return false
-
- if (!_supportsDaylightSavingTime || _adjustmentRules == null)
- {
- return false;
- }
-
- DateTime adjustedTime;
- //
- // handle any Local/Utc special cases...
- //
- if (dateTime.Kind == DateTimeKind.Local)
- {
- adjustedTime = TimeZoneInfo.ConvertTime(dateTime, cachedData.Local, this, flags, cachedData);
- }
- else if (dateTime.Kind == DateTimeKind.Utc)
- {
- if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc)
- {
- // simple always false case: TimeZoneInfo.Utc.IsDaylightSavingTime(dateTime, flags);
- return false;
- }
- else
- {
- //
- // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a
- // special Loss-Less case.
- //
- Boolean isDaylightSavings;
- GetUtcOffsetFromUtc(dateTime, this, out isDaylightSavings);
- return isDaylightSavings;
- }
- }
- else
- {
- adjustedTime = dateTime;
- }
-
- //
- // handle the normal cases...
- //
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
- if (rule != null && rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
- return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
- }
- else
- {
- return false;
- }
- }
-
- //
- // IsInvalidTime -
- //
- // returns true when dateTime falls into a "hole in time".
- //
- public Boolean IsInvalidTime(DateTime dateTime)
- {
- Boolean isInvalid = false;
-
- if ((dateTime.Kind == DateTimeKind.Unspecified)
- || (dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local))
- {
- // only check Unspecified and (Local when this TimeZoneInfo instance is Local)
- AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime);
-
-
- if (rule != null && rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule);
- isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
- }
- else
- {
- isInvalid = false;
- }
- }
-
- return isInvalid;
- }
-
- //
- // ClearCachedData -
- //
- // Clears data from static members
- //
- public static void ClearCachedData()
- {
- // Clear a fresh instance of cached data
- s_cachedData = new CachedData();
- }
-
- //
- // ConvertTimeBySystemTimeZoneId -
- //
- // Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone
- //
- public static DateTimeOffset ConvertTimeBySystemTimeZoneId(DateTimeOffset dateTimeOffset, String destinationTimeZoneId)
- {
- return ConvertTime(dateTimeOffset, FindSystemTimeZoneById(destinationTimeZoneId));
- }
-
- public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, String destinationTimeZoneId)
- {
- return ConvertTime(dateTime, FindSystemTimeZoneById(destinationTimeZoneId));
- }
-
- public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, String sourceTimeZoneId, String destinationTimeZoneId)
- {
- if (dateTime.Kind == DateTimeKind.Local && String.Compare(sourceTimeZoneId, TimeZoneInfo.Local.Id, StringComparison.OrdinalIgnoreCase) == 0)
- {
- // TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData.
- // Take snapshot of cached data to guarantee this method will not be impacted by the ClearCachedData call.
- // Without the snapshot, there is a chance that ConvertTime will throw since 'source' won't
- // be reference equal to the new TimeZoneInfo.Local
- //
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, cachedData.Local, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, cachedData);
- }
- else if (dateTime.Kind == DateTimeKind.Utc && String.Compare(sourceTimeZoneId, TimeZoneInfo.Utc.Id, StringComparison.OrdinalIgnoreCase) == 0)
- {
- // TimeZoneInfo.Utc can be cleared by another thread calling TimeZoneInfo.ClearCachedData.
- // Take snapshot of cached data to guarantee this method will not be impacted by the ClearCachedData call.
- // Without the snapshot, there is a chance that ConvertTime will throw since 'source' won't
- // be reference equal to the new TimeZoneInfo.Utc
- //
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, s_utcTimeZone, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, cachedData);
- }
- else
- {
- return ConvertTime(dateTime, FindSystemTimeZoneById(sourceTimeZoneId), FindSystemTimeZoneById(destinationTimeZoneId));
- }
- }
-
- //
- // ConvertTime -
- //
- // Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone
- //
-
- public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
- {
- if (destinationTimeZone == null)
- {
- throw new ArgumentNullException(nameof(destinationTimeZone));
- }
-
- // calculate the destination time zone offset
- DateTime utcDateTime = dateTimeOffset.UtcDateTime;
- TimeSpan destinationOffset = GetUtcOffsetFromUtc(utcDateTime, destinationTimeZone);
-
- // check for overflow
- Int64 ticks = utcDateTime.Ticks + destinationOffset.Ticks;
-
- if (ticks > DateTimeOffset.MaxValue.Ticks)
- {
- return DateTimeOffset.MaxValue;
- }
- else if (ticks < DateTimeOffset.MinValue.Ticks)
- {
- return DateTimeOffset.MinValue;
- }
- else
- {
- return new DateTimeOffset(ticks, destinationOffset);
- }
- }
-
- public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo destinationTimeZone)
- {
- if (destinationTimeZone == null)
- {
- throw new ArgumentNullException(nameof(destinationTimeZone));
- }
-
- // Special case to give a way clearing the cache without exposing ClearCachedData()
- if (dateTime.Ticks == 0)
- {
- ClearCachedData();
- }
-
- CachedData cachedData = s_cachedData;
- if (dateTime.Kind == DateTimeKind.Utc)
- {
- return ConvertTime(dateTime, s_utcTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, cachedData);
- }
- else
- {
- return ConvertTime(dateTime, cachedData.Local, destinationTimeZone, TimeZoneInfoOptions.None, cachedData);
- }
- }
-
- public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
- {
- return ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, s_cachedData);
- }
-
- internal static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags)
- {
- return ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, flags, s_cachedData);
- }
-
- private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags, CachedData cachedData)
- {
- if (sourceTimeZone == null)
- {
- throw new ArgumentNullException(nameof(sourceTimeZone));
- }
-
- if (destinationTimeZone == null)
- {
- throw new ArgumentNullException(nameof(destinationTimeZone));
- }
-
- DateTimeKind sourceKind = cachedData.GetCorrespondingKind(sourceTimeZone);
- if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && (dateTime.Kind != DateTimeKind.Unspecified) && (dateTime.Kind != sourceKind))
- {
- throw new ArgumentException(SR.Argument_ConvertMismatch, nameof(sourceTimeZone));
- }
-
- //
- // check to see if the DateTime is in an invalid time range. This check
- // requires the current AdjustmentRule and DaylightTime - which are also
- // needed to calculate 'sourceOffset' in the normal conversion case.
- // By calculating the 'sourceOffset' here we improve the
- // performance for the normal case at the expense of the 'ArgumentException'
- // case and Loss-less Local special cases.
- //
- AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime);
- TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset;
-
- if (sourceRule != null)
- {
- sourceOffset = sourceOffset + sourceRule.BaseUtcOffsetDelta;
- if (sourceRule.HasDaylightSaving)
- {
- Boolean sourceIsDaylightSavings = false;
- DaylightTimeStruct sourceDaylightTime = GetDaylightTime(dateTime.Year, sourceRule);
-
- // 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
- // period that supports DST
- if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && GetIsInvalidTime(dateTime, sourceRule, sourceDaylightTime))
- {
- throw new ArgumentException(SR.Argument_DateTimeIsInvalid, nameof(dateTime));
- }
- sourceIsDaylightSavings = GetIsDaylightSavings(dateTime, sourceRule, sourceDaylightTime, flags);
-
- // adjust the sourceOffset according to the Adjustment Rule / Daylight Saving Rule
- sourceOffset += (sourceIsDaylightSavings ? sourceRule.DaylightDelta : TimeSpan.Zero /*FUTURE: sourceRule.StandardDelta*/);
- }
- }
-
- DateTimeKind targetKind = cachedData.GetCorrespondingKind(destinationTimeZone);
-
- // handle the special case of Loss-less Local->Local and UTC->UTC)
- if (dateTime.Kind != DateTimeKind.Unspecified && sourceKind != DateTimeKind.Unspecified
- && sourceKind == targetKind)
- {
- return dateTime;
- }
-
- Int64 utcTicks = dateTime.Ticks - sourceOffset.Ticks;
-
- // handle the normal case by converting from 'source' to UTC and then to 'target'
- Boolean isAmbiguousLocalDst = false;
- DateTime targetConverted = ConvertUtcToTimeZone(utcTicks, destinationTimeZone, out isAmbiguousLocalDst);
-
- if (targetKind == DateTimeKind.Local)
- {
- // Because the ticks conversion between UTC and local is lossy, we need to capture whether the
- // time is in a repeated hour so that it can be passed to the DateTime constructor.
- return new DateTime(targetConverted.Ticks, DateTimeKind.Local, isAmbiguousLocalDst);
- }
- else
- {
- return new DateTime(targetConverted.Ticks, targetKind);
- }
- }
-
- //
- // ConvertTimeFromUtc -
- //
- // Converts the value of a DateTime object from Coordinated Universal Time (UTC) to
- // the destinationTimeZone.
- //
- public static DateTime ConvertTimeFromUtc(DateTime dateTime, TimeZoneInfo destinationTimeZone)
- {
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, s_utcTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, cachedData);
- }
-
- //
- // ConvertTimeToUtc -
- //
- // Converts the value of a DateTime object to Coordinated Universal Time (UTC).
- //
- public static DateTime ConvertTimeToUtc(DateTime dateTime)
- {
- if (dateTime.Kind == DateTimeKind.Utc)
- {
- return dateTime;
- }
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, TimeZoneInfoOptions.None, cachedData);
- }
-
- internal static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
- {
- if (dateTime.Kind == DateTimeKind.Utc)
- {
- return dateTime;
- }
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags, cachedData);
- }
-
- public static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfo sourceTimeZone)
- {
- CachedData cachedData = s_cachedData;
- return ConvertTime(dateTime, sourceTimeZone, s_utcTimeZone, TimeZoneInfoOptions.None, cachedData);
- }
-
- //
- // IEquatable.Equals -
- //
- // returns value equality. Equals does not compare any localizable
- // String objects (DisplayName, StandardName, DaylightName).
- //
- public bool Equals(TimeZoneInfo other)
- {
- return (other != null && String.Compare(_id, other._id, StringComparison.OrdinalIgnoreCase) == 0 && HasSameRules(other));
- }
-
- public override bool Equals(object obj)
- {
- return Equals(obj as TimeZoneInfo);
- }
-
- //
- // GetHashCode -
- //
- public override int GetHashCode()
- {
- return StringComparer.OrdinalIgnoreCase.GetHashCode(_id);
- }
-
- //
- // GetSystemTimeZones -
- //
- // returns a ReadOnlyCollection<TimeZoneInfo> containing all valid TimeZone's
- // from the local machine. The entries in the collection are sorted by
- // 'DisplayName'.
- //
- // This method does *not* throw TimeZoneNotFoundException or
- // InvalidTimeZoneException.
- //
- public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones()
- {
- CachedData cachedData = s_cachedData;
-
- lock (cachedData)
- {
- if (cachedData._readOnlySystemTimeZones == null)
- {
- PopulateAllSystemTimeZones(cachedData);
- cachedData._allSystemTimeZonesRead = true;
-
- List<TimeZoneInfo> list;
- if (cachedData._systemTimeZones != null)
- {
- // return a collection of the cached system time zones
- list = new List<TimeZoneInfo>(cachedData._systemTimeZones.Count);
- foreach (var pair in cachedData._systemTimeZones)
- {
- list.Add(pair.Value);
- }
- }
- else
- {
- // return an empty collection
- list = new List<TimeZoneInfo>();
- }
-
- // sort and copy the TimeZoneInfo's into a ReadOnlyCollection for the user
- list.Sort((x, y) =>
- {
- // sort by BaseUtcOffset first and by DisplayName second - this is similar to the Windows Date/Time control panel
- int comparison = x.BaseUtcOffset.CompareTo(y.BaseUtcOffset);
- return comparison == 0 ? string.CompareOrdinal(x.DisplayName, y.DisplayName) : comparison;
- });
-
- cachedData._readOnlySystemTimeZones = new ReadOnlyCollection<TimeZoneInfo>(list);
- }
- }
- return cachedData._readOnlySystemTimeZones;
- }
-
- //
- // HasSameRules -
- //
- // Value equality on the "adjustmentRules" array
- //
- public Boolean HasSameRules(TimeZoneInfo other)
- {
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
-
- // check the utcOffset and supportsDaylightSavingTime members
-
- if (_baseUtcOffset != other._baseUtcOffset
- || _supportsDaylightSavingTime != other._supportsDaylightSavingTime)
- {
- return false;
- }
-
- bool sameRules;
- AdjustmentRule[] currentRules = _adjustmentRules;
- AdjustmentRule[] otherRules = other._adjustmentRules;
-
- sameRules = (currentRules == null && otherRules == null)
- || (currentRules != null && otherRules != null);
-
- if (!sameRules)
- {
- // AdjustmentRule array mismatch
- return false;
- }
-
- if (currentRules != null)
- {
- if (currentRules.Length != otherRules.Length)
- {
- // AdjustmentRule array length mismatch
- return false;
- }
-
- for (int i = 0; i < currentRules.Length; i++)
- {
- if (!(currentRules[i]).Equals(otherRules[i]))
- {
- // AdjustmentRule value-equality mismatch
- return false;
- }
- }
- }
- return sameRules;
- }
-
- //
- // Local -
- //
- // returns a TimeZoneInfo instance that represents the local time on the machine.
- // Accessing this property may throw InvalidTimeZoneException or COMException
- // if the machine is in an unstable or corrupt state.
- //
- public static TimeZoneInfo Local
- {
- get
- {
- return s_cachedData.Local;
- }
- }
-
- //
- // ToString -
- //
- // returns the DisplayName:
- // "(GMT-08:00) Pacific Time (US & Canada); Tijuana"
- //
- public override string ToString()
- {
- return this.DisplayName;
- }
-
- //
- // ToSerializedString -
- //
- // "TimeZoneInfo" := TimeZoneInfo Data;[AdjustmentRule Data 1];...;[AdjustmentRule Data N]
- //
- // "TimeZoneInfo Data" := <_id>;<_baseUtcOffset>;<_displayName>;
- // <_standardDisplayName>;<_daylightDispayName>;
- //
- // "AdjustmentRule Data" := <DateStart>;<DateEnd>;<DaylightDelta>;
- // [TransitionTime Data DST Start]
- // [TransitionTime Data DST End]
- //
- // "TransitionTime Data" += <DaylightStartTimeOfDat>;<Month>;<Week>;<DayOfWeek>;<Day>
- //
- public String ToSerializedString()
- {
- return StringSerializer.GetSerializedString(this);
- }
-
- //
- // FromSerializedString -
- //
- public static TimeZoneInfo FromSerializedString(string source)
- {
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
- if (source.Length == 0)
- {
- throw new ArgumentException(SR.Argument_InvalidSerializedString, nameof(source));
- }
-
- return StringSerializer.GetDeserializedTimeZoneInfo(source);
- }
-
- //
- // Utc -
- //
- // returns a TimeZoneInfo instance that represents Universal Coordinated Time (UTC)
- //
- public static TimeZoneInfo Utc
- {
- get
- {
- return s_utcTimeZone;
- }
- }
-
- // -------- SECTION: factory methods -----------------*
-
- //
- // CreateCustomTimeZone -
- //
- // returns a simple TimeZoneInfo instance that does
- // not support Daylight Saving Time
- //
- public static TimeZoneInfo CreateCustomTimeZone(
- String id,
- TimeSpan baseUtcOffset,
- String displayName,
- String standardDisplayName)
- {
- return new TimeZoneInfo(
- id,
- baseUtcOffset,
- displayName,
- standardDisplayName,
- standardDisplayName,
- null,
- false);
- }
-
- //
- // CreateCustomTimeZone -
- //
- // returns a TimeZoneInfo instance that may
- // support Daylight Saving Time
- //
- public static TimeZoneInfo CreateCustomTimeZone(
- String id,
- TimeSpan baseUtcOffset,
- String displayName,
- String standardDisplayName,
- String daylightDisplayName,
- AdjustmentRule[] adjustmentRules)
- {
- return CreateCustomTimeZone(
- id,
- baseUtcOffset,
- displayName,
- standardDisplayName,
- daylightDisplayName,
- adjustmentRules,
- false);
- }
-
- //
- // CreateCustomTimeZone -
- //
- // returns a TimeZoneInfo instance that may
- // support Daylight Saving Time
- //
- // This class factory method is identical to the
- // TimeZoneInfo private constructor
- //
- public static TimeZoneInfo CreateCustomTimeZone(
- String id,
- TimeSpan baseUtcOffset,
- String displayName,
- String standardDisplayName,
- String daylightDisplayName,
- AdjustmentRule[] adjustmentRules,
- Boolean disableDaylightSavingTime)
- {
- if (!disableDaylightSavingTime && adjustmentRules?.Length > 0)
- {
- adjustmentRules = (AdjustmentRule[])adjustmentRules.Clone();
- }
-
- return new TimeZoneInfo(
- id,
- baseUtcOffset,
- displayName,
- standardDisplayName,
- daylightDisplayName,
- adjustmentRules,
- disableDaylightSavingTime);
- }
-
- // ----- SECTION: internal instance utility methods ----------------*
-
-
- // assumes dateTime is in the current time zone's time
- private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime)
- {
- if (_adjustmentRules == null || _adjustmentRules.Length == 0)
- {
- return null;
- }
-
- // Only check the whole-date portion of the dateTime -
- // This is because the AdjustmentRule DateStart & DateEnd are stored as
- // Date-only values {4/2/2006 - 10/28/2006} but actually represent the
- // time span {4/2/2006@00:00:00.00000 - 10/28/2006@23:59:59.99999}
- DateTime date = dateTime.Date;
-
- for (int i = 0; i < _adjustmentRules.Length; i++)
- {
- if (_adjustmentRules[i].DateStart <= date && _adjustmentRules[i].DateEnd >= date)
- {
- return _adjustmentRules[i];
- }
- }
-
- return null;
- }
-
- //
- // ConvertUtcToTimeZone -
- //
- // Helper function that converts a dateTime from UTC into the destinationTimeZone
- //
- // * returns DateTime.MaxValue when the converted value is too large
- // * returns DateTime.MinValue when the converted value is too small
- //
- private static DateTime ConvertUtcToTimeZone(Int64 ticks, TimeZoneInfo destinationTimeZone, out Boolean isAmbiguousLocalDst)
- {
- DateTime utcConverted;
- DateTime localConverted;
-
- // utcConverted is used to calculate the UTC offset in the destinationTimeZone
- if (ticks > DateTime.MaxValue.Ticks)
- {
- utcConverted = DateTime.MaxValue;
- }
- else if (ticks < DateTime.MinValue.Ticks)
- {
- utcConverted = DateTime.MinValue;
- }
- else
- {
- utcConverted = new DateTime(ticks);
- }
-
- // verify the time is between MinValue and MaxValue in the new time zone
- TimeSpan offset = GetUtcOffsetFromUtc(utcConverted, destinationTimeZone, out isAmbiguousLocalDst);
- ticks += offset.Ticks;
-
- if (ticks > DateTime.MaxValue.Ticks)
- {
- localConverted = DateTime.MaxValue;
- }
- else if (ticks < DateTime.MinValue.Ticks)
- {
- localConverted = DateTime.MinValue;
- }
- else
- {
- localConverted = new DateTime(ticks);
- }
- return localConverted;
- }
-
- //
- // GetDaylightTime -
- //
- // Helper function that returns a DaylightTimeStruct from a year and AdjustmentRule
- //
- private static DaylightTimeStruct GetDaylightTime(Int32 year, AdjustmentRule rule)
- {
- TimeSpan delta = rule.DaylightDelta;
- DateTime startTime = TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
- DateTime endTime = TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
- return new DaylightTimeStruct(startTime, endTime, delta);
- }
-
- //
- // GetIsDaylightSavings -
- //
- // Helper function that checks if a given dateTime is in Daylight Saving Time (DST)
- // This function assumes the dateTime and AdjustmentRule are both in the same time zone
- //
- private static Boolean GetIsDaylightSavings(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime, TimeZoneInfoOptions flags)
- {
- if (rule == null)
- {
- return false;
- }
-
- DateTime startTime;
- DateTime endTime;
-
- if (time.Kind == DateTimeKind.Local)
- {
- // startTime and endTime represent the period from either the start of DST to the end and ***includes*** the
- // potentially overlapped times
- startTime = rule.IsStartDateMarkerForBeginningOfYear() ? new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) : daylightTime.Start + daylightTime.Delta;
- endTime = rule.IsEndDateMarkerForEndOfYear() ? new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) : daylightTime.End;
- }
- else
- {
- // startTime and endTime represent the period from either the start of DST to the end and
- // ***does not include*** the potentially overlapped times
- //
- // -=-=-=-=-=- Pacific Standard Time -=-=-=-=-=-=-
- // April 2, 2006 October 29, 2006
- // 2AM 3AM 1AM 2AM
- // | +1 hr | | -1 hr |
- // | <invalid time> | | <ambiguous time> |
- // [========== DST ========>)
- //
- // -=-=-=-=-=- Some Weird Time Zone -=-=-=-=-=-=-
- // April 2, 2006 October 29, 2006
- // 1AM 2AM 2AM 3AM
- // | -1 hr | | +1 hr |
- // | <ambiguous time> | | <invalid time> |
- // [======== DST ========>)
- //
- Boolean invalidAtStart = rule.DaylightDelta > TimeSpan.Zero;
- startTime = rule.IsStartDateMarkerForBeginningOfYear() ? new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) : daylightTime.Start + (invalidAtStart ? rule.DaylightDelta : TimeSpan.Zero); /* FUTURE: - rule.StandardDelta; */
- endTime = rule.IsEndDateMarkerForEndOfYear() ? new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) : daylightTime.End + (invalidAtStart ? -rule.DaylightDelta : TimeSpan.Zero);
- }
-
- Boolean isDst = CheckIsDst(startTime, time, endTime, false);
-
- // If this date was previously converted from a UTC date and we were able to detect that the local
- // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity.
- if (isDst && time.Kind == DateTimeKind.Local)
- {
- // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the
- // clock back. It is theoretically possible to have a positive delta, (which would really be daylight
- // reduction time), where you would have to wind the clock back in the begnning.
- if (GetIsAmbiguousTime(time, rule, daylightTime))
- {
- isDst = time.IsAmbiguousDaylightSavingTime();
- }
- }
-
- return isDst;
- }
-
- //
- // GetIsDaylightSavingsFromUtc -
- //
- // Helper function that checks if a given dateTime is in Daylight Saving Time (DST)
- // This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone
- //
- private static Boolean GetIsDaylightSavingsFromUtc(DateTime time, Int32 Year, TimeSpan utc, AdjustmentRule rule, out Boolean isAmbiguousLocalDst, TimeZoneInfo zone)
- {
- isAmbiguousLocalDst = false;
-
- if (rule == null)
- {
- return false;
- }
-
- // Get the daylight changes for the year of the specified time.
- TimeSpan offset = utc + rule.BaseUtcOffsetDelta; /* FUTURE: + rule.StandardDelta; */
- DaylightTimeStruct daylightTime = GetDaylightTime(Year, rule);
-
- // The start and end times represent the range of universal times that are in DST for that year.
- // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
- // the unusual case of a negative daylight savings delta.
- // We need to handle the case if the current rule has daylight saving end by the end of year. If so, we need to check if next year starts with daylight saving on
- // and get the actual daylight saving end time. Here is example for such case:
- // Converting the UTC datetime "12/31/2011 8:00:00 PM" to "(UTC+03:00) Moscow, St. Petersburg, Volgograd (RTZ 2)" zone.
- // In 2011 the daylight saving will go through the end of the year. If we use the end of 2011 as the daylight saving end,
- // that will fail the conversion because the UTC time +4 hours (3 hours for the zone UTC offset and 1 hour for daylight saving) will move us to the next year "1/1/2012 12:00 AM",
- // checking against the end of 2011 will tell we are not in daylight saving which is wrong and the conversion will be off by one hour.
- // Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
-
- bool ignoreYearAdjustment = false;
- DateTime startTime;
- if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year)
- {
- AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.Start.Year - 1, 12, 31));
- if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear())
- {
- DaylightTimeStruct previousDaylightTime = GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
- startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
- ignoreYearAdjustment = true;
- }
- else
- {
- startTime = new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) - offset;
- }
- }
- else
- {
- startTime = daylightTime.Start - offset;
- }
-
- DateTime endTime;
- if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year)
- {
- AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.End.Year + 1, 1, 1));
- if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear())
- {
- if (nextYearRule.IsEndDateMarkerForEndOfYear())
- {// next year end with daylight saving on too
- endTime = new DateTime(daylightTime.End.Year + 1, 12, 31) - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
- }
- else
- {
- DaylightTimeStruct nextdaylightTime = GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
- endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
- }
- ignoreYearAdjustment = true;
- }
- else
- {
- endTime = new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) - offset - rule.DaylightDelta; ;
- }
- }
- else
- {
- endTime = daylightTime.End - offset - rule.DaylightDelta;
- }
-
- DateTime ambiguousStart;
- DateTime ambiguousEnd;
- if (daylightTime.Delta.Ticks > 0)
- {
- ambiguousStart = endTime - daylightTime.Delta;
- ambiguousEnd = endTime;
- }
- else
- {
- ambiguousStart = startTime;
- ambiguousEnd = startTime - daylightTime.Delta;
- }
-
- Boolean isDst = CheckIsDst(startTime, time, endTime, ignoreYearAdjustment);
-
- // See if the resulting local time becomes ambiguous. This must be captured here or the
- // DateTime will not be able to round-trip back to UTC accurately.
- if (isDst)
- {
- isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
-
- if (!isAmbiguousLocalDst && ambiguousStart.Year != ambiguousEnd.Year)
- {
- // there exists an extreme corner case where the start or end period is on a year boundary and
- // because of this the comparison above might have been performed for a year-early or a year-later
- // than it should have been.
- DateTime ambiguousStartModified;
- DateTime ambiguousEndModified;
- try
- {
- ambiguousStartModified = ambiguousStart.AddYears(1);
- ambiguousEndModified = ambiguousEnd.AddYears(1);
- isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
- }
- catch (ArgumentOutOfRangeException) { }
-
- if (!isAmbiguousLocalDst)
- {
- try
- {
- ambiguousStartModified = ambiguousStart.AddYears(-1);
- ambiguousEndModified = ambiguousEnd.AddYears(-1);
- isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd);
- }
- catch (ArgumentOutOfRangeException) { }
- }
- }
- }
-
- return isDst;
- }
-
- private static Boolean CheckIsDst(DateTime startTime, DateTime time, DateTime endTime, bool ignoreYearAdjustment)
- {
- Boolean isDst;
-
- if (!ignoreYearAdjustment)
- {
- int startTimeYear = startTime.Year;
- int endTimeYear = endTime.Year;
-
- if (startTimeYear != endTimeYear)
- {
- endTime = endTime.AddYears(startTimeYear - endTimeYear);
- }
-
- int timeYear = time.Year;
-
- if (startTimeYear != timeYear)
- {
- time = time.AddYears(startTimeYear - timeYear);
- }
- }
-
- if (startTime > endTime)
- {
- // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
- // Note, the summer in the southern hemisphere begins late in the year.
- isDst = (time < endTime || time >= startTime);
- }
- else
- {
- // In northern hemisphere, the daylight saving time starts in the middle of the year.
- isDst = (time >= startTime && time < endTime);
- }
- return isDst;
- }
-
- //
- // GetIsAmbiguousTime(DateTime dateTime, AdjustmentRule rule, DaylightTimeStruct daylightTime) -
- //
- // returns true when the dateTime falls into an ambiguous time range.
- // For example, in Pacific Standard Time on Sunday, October 29, 2006 time jumps from
- // 2AM to 1AM. This means the timeline on Sunday proceeds as follows:
- // 12AM ... [1AM ... 1:59:59AM -> 1AM ... 1:59:59AM] 2AM ... 3AM ...
- //
- // In this example, any DateTime values that fall into the [1AM - 1:59:59AM] range
- // are ambiguous; as it is unclear if these times are in Daylight Saving Time.
- //
- private static Boolean GetIsAmbiguousTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime)
- {
- Boolean isAmbiguous = false;
- if (rule == null || rule.DaylightDelta == TimeSpan.Zero)
- {
- return isAmbiguous;
- }
-
- DateTime startAmbiguousTime;
- DateTime endAmbiguousTime;
-
- // if at DST start we transition forward in time then there is an ambiguous time range at the DST end
- if (rule.DaylightDelta > TimeSpan.Zero)
- {
- if (rule.IsEndDateMarkerForEndOfYear())
- {
- // year end with daylight on so there is no ambiguous time
- return false;
- }
- startAmbiguousTime = daylightTime.End;
- endAmbiguousTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */
- }
- else
- {
- if (rule.IsStartDateMarkerForBeginningOfYear())
- {
- // year start with daylight on so there is no ambiguous time
- return false;
- }
- startAmbiguousTime = daylightTime.Start;
- endAmbiguousTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */
- }
-
- isAmbiguous = (time >= endAmbiguousTime && time < startAmbiguousTime);
-
- if (!isAmbiguous && startAmbiguousTime.Year != endAmbiguousTime.Year)
- {
- // there exists an extreme corner case where the start or end period is on a year boundary and
- // because of this the comparison above might have been performed for a year-early or a year-later
- // than it should have been.
- DateTime startModifiedAmbiguousTime;
- DateTime endModifiedAmbiguousTime;
- try
- {
- startModifiedAmbiguousTime = startAmbiguousTime.AddYears(1);
- endModifiedAmbiguousTime = endAmbiguousTime.AddYears(1);
- isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime);
- }
- catch (ArgumentOutOfRangeException) { }
-
- if (!isAmbiguous)
- {
- try
- {
- startModifiedAmbiguousTime = startAmbiguousTime.AddYears(-1);
- endModifiedAmbiguousTime = endAmbiguousTime.AddYears(-1);
- isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime);
- }
- catch (ArgumentOutOfRangeException) { }
- }
- }
- return isAmbiguous;
- }
-
- //
- // GetIsInvalidTime -
- //
- // Helper function that checks if a given DateTime is in an invalid time ("time hole")
- // A "time hole" occurs at a DST transition point when time jumps forward;
- // For example, in Pacific Standard Time on Sunday, April 2, 2006 time jumps from
- // 1:59:59.9999999 to 3AM. The time range 2AM to 2:59:59.9999999AM is the "time hole".
- // A "time hole" is not limited to only occurring at the start of DST, and may occur at
- // the end of DST as well.
- //
- private static Boolean GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime)
- {
- Boolean isInvalid = false;
- if (rule == null || rule.DaylightDelta == TimeSpan.Zero)
- {
- return isInvalid;
- }
-
- DateTime startInvalidTime;
- DateTime endInvalidTime;
-
- // if at DST start we transition forward in time then there is an ambiguous time range at the DST end
- if (rule.DaylightDelta < TimeSpan.Zero)
- {
- // if the year ends with daylight saving on then there cannot be any time-hole's in that year.
- if (rule.IsEndDateMarkerForEndOfYear())
- return false;
-
- startInvalidTime = daylightTime.End;
- endInvalidTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */
- }
- else
- {
- // if the year starts with daylight saving on then there cannot be any time-hole's in that year.
- if (rule.IsStartDateMarkerForBeginningOfYear())
- return false;
-
- startInvalidTime = daylightTime.Start;
- endInvalidTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */
- }
-
- isInvalid = (time >= startInvalidTime && time < endInvalidTime);
-
- if (!isInvalid && startInvalidTime.Year != endInvalidTime.Year)
- {
- // there exists an extreme corner case where the start or end period is on a year boundary and
- // because of this the comparison above might have been performed for a year-early or a year-later
- // than it should have been.
- DateTime startModifiedInvalidTime;
- DateTime endModifiedInvalidTime;
- try
- {
- startModifiedInvalidTime = startInvalidTime.AddYears(1);
- endModifiedInvalidTime = endInvalidTime.AddYears(1);
- isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime);
- }
- catch (ArgumentOutOfRangeException) { }
-
- if (!isInvalid)
- {
- try
- {
- startModifiedInvalidTime = startInvalidTime.AddYears(-1);
- endModifiedInvalidTime = endInvalidTime.AddYears(-1);
- isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime);
- }
- catch (ArgumentOutOfRangeException) { }
- }
- }
- return isInvalid;
- }
-
- //
- // GetUtcOffset -
- //
- // Helper function that calculates the UTC offset for a dateTime in a timeZone.
- // This function assumes that the dateTime is already converted into the timeZone.
- //
- private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags)
- {
- TimeSpan baseOffset = zone.BaseUtcOffset;
- AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time);
-
- if (rule != null)
- {
- baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
- if (rule.HasDaylightSaving)
- {
- DaylightTimeStruct daylightTime = GetDaylightTime(time.Year, rule);
- Boolean isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
- baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
- }
- }
-
- return baseOffset;
- }
-
- // DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
- internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
- {
- Boolean isDaylightSavings = false;
- isAmbiguousLocalDst = false;
- TimeSpan baseOffset;
- int timeYear = time.Year;
-
- OffsetAndRule match = s_cachedData.GetOneYearLocalFromUtc(timeYear);
- baseOffset = match.offset;
-
- if (match.rule != null)
- {
- baseOffset = baseOffset + match.rule.BaseUtcOffsetDelta;
- if (match.rule.HasDaylightSaving)
- {
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.offset, match.rule, out isAmbiguousLocalDst, TimeZoneInfo.Local);
- baseOffset += (isDaylightSavings ? match.rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
- }
- }
- return baseOffset;
- }
-
- //
- // GetUtcOffsetFromUtc -
- //
- // Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone.
- // This function assumes that the dateTime is represented in UTC and has *not*
- // already been converted into the timeZone.
- //
- private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone)
- {
- Boolean isDaylightSavings;
- return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings);
- }
-
- private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings)
- {
- Boolean isAmbiguousLocalDst;
- return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings, out isAmbiguousLocalDst);
- }
-
- internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
- {
- isDaylightSavings = false;
- isAmbiguousLocalDst = false;
- TimeSpan baseOffset = zone.BaseUtcOffset;
- Int32 year;
- AdjustmentRule rule;
-
- if (time > s_maxDateOnly)
- {
- rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue);
- year = 9999;
- }
- else if (time < s_minDateOnly)
- {
- rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue);
- year = 1;
- }
- else
- {
- DateTime targetTime = time + baseOffset;
-
- // As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
- // sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
- // "Libya Standard Time" when used with the date 2011-12-31T23:59:59.9999999Z
- // "W. Australia Standard Time" used with date 2005-12-31T23:59:00.0000000Z
- year = targetTime.Year;
-
- rule = zone.GetAdjustmentRuleForTime(targetTime);
- }
-
- if (rule != null)
- {
- baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
- if (rule.HasDaylightSaving)
- {
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, out isAmbiguousLocalDst, zone);
- baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
- }
- }
-
- return baseOffset;
- }
-
- //
- // TransitionTimeToDateTime -
- //
- // Helper function that converts a year and TransitionTime into a DateTime
- //
- internal static DateTime TransitionTimeToDateTime(Int32 year, TransitionTime transitionTime)
- {
- DateTime value;
- DateTime timeOfDay = transitionTime.TimeOfDay;
-
- if (transitionTime.IsFixedDateRule)
- {
- // create a DateTime from the passed in year and the properties on the transitionTime
-
- // if the day is out of range for the month then use the last day of the month
- Int32 day = DateTime.DaysInMonth(year, transitionTime.Month);
-
- value = new DateTime(year, transitionTime.Month, (day < transitionTime.Day) ? day : transitionTime.Day,
- timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
- }
- else
- {
- if (transitionTime.Week <= 4)
- {
- //
- // Get the (transitionTime.Week)th Sunday.
- //
- value = new DateTime(year, transitionTime.Month, 1,
- timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
-
- int dayOfWeek = (int)value.DayOfWeek;
- int delta = (int)transitionTime.DayOfWeek - dayOfWeek;
- if (delta < 0)
- {
- delta += 7;
- }
- delta += 7 * (transitionTime.Week - 1);
-
- if (delta > 0)
- {
- value = value.AddDays(delta);
- }
- }
- else
- {
- //
- // If TransitionWeek is greater than 4, we will get the last week.
- //
- Int32 daysInMonth = DateTime.DaysInMonth(year, transitionTime.Month);
- value = new DateTime(year, transitionTime.Month, daysInMonth,
- timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
-
- // This is the day of week for the last day of the month.
- int dayOfWeek = (int)value.DayOfWeek;
- int delta = dayOfWeek - (int)transitionTime.DayOfWeek;
- if (delta < 0)
- {
- delta += 7;
- }
-
- if (delta > 0)
- {
- value = value.AddDays(-delta);
- }
- }
- }
- return value;
- }
-
- //
- // UtcOffsetOutOfRange -
- //
- // Helper function that validates the TimeSpan is within +/- 14.0 hours
- //
- internal static Boolean UtcOffsetOutOfRange(TimeSpan offset)
- {
- return (offset.TotalHours < -14.0 || offset.TotalHours > 14.0);
- }
-
- //
- // ValidateTimeZoneInfo -
- //
- // Helper function that performs all of the validation checks for the
- // factory methods and deserialization callback
- //
- // returns a Boolean indicating whether the AdjustmentRule[] supports DST
- //
- private static void ValidateTimeZoneInfo(
- String id,
- TimeSpan baseUtcOffset,
- AdjustmentRule[] adjustmentRules,
- out Boolean adjustmentRulesSupportDst)
- {
- if (id == null)
- {
- throw new ArgumentNullException(nameof(id));
- }
-
- if (id.Length == 0)
- {
- throw new ArgumentException(SR.Format(SR.Argument_InvalidId, id), nameof(id));
- }
-
- if (UtcOffsetOutOfRange(baseUtcOffset))
- {
- throw new ArgumentOutOfRangeException(nameof(baseUtcOffset), SR.ArgumentOutOfRange_UtcOffset);
- }
-
- if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
- {
- throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(baseUtcOffset));
- }
-
- adjustmentRulesSupportDst = false;
-
- //
- // "adjustmentRules" can either be null or a valid array of AdjustmentRule objects.
- // A valid array is one that does not contain any null elements and all elements
- // are sorted in chronological order
- //
-
- if (adjustmentRules != null && adjustmentRules.Length != 0)
- {
- adjustmentRulesSupportDst = true;
- AdjustmentRule prev = null;
- AdjustmentRule current = null;
- for (int i = 0; i < adjustmentRules.Length; i++)
- {
- prev = current;
- current = adjustmentRules[i];
-
- if (current == null)
- {
- throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls);
- }
-
- // FUTURE: check to see if this rule supports Daylight Saving Time
- // adjustmentRulesSupportDst = adjustmentRulesSupportDst || current.SupportsDaylightSavingTime;
- // FUTURE: test baseUtcOffset + current.StandardDelta
-
- if (UtcOffsetOutOfRange(baseUtcOffset + current.DaylightDelta))
- {
- throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta);
- }
-
-
- if (prev != null && current.DateStart <= prev.DateEnd)
- {
- // verify the rules are in chronological order and the DateStart/DateEnd do not overlap
- throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesOutOfOrder);
- }
- }
- }
- }
-
-
- // ----- SECTION: private serialization instance methods ----------------*
-
- void IDeserializationCallback.OnDeserialization(Object sender)
- {
- try
- {
- Boolean adjustmentRulesSupportDst;
- ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out adjustmentRulesSupportDst);
-
- if (adjustmentRulesSupportDst != _supportsDaylightSavingTime)
- {
- throw new SerializationException(String.Format(SR.Serialization_CorruptField, "SupportsDaylightSavingTime"));
- }
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- catch (InvalidTimeZoneException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
-
-
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- info.AddValue("Id", _id); // Do not rename (binary serialization)
- info.AddValue("DisplayName", _displayName); // Do not rename (binary serialization)
- info.AddValue("StandardName", _standardDisplayName); // Do not rename (binary serialization)
- info.AddValue("DaylightName", _daylightDisplayName); // Do not rename (binary serialization)
- info.AddValue("BaseUtcOffset", _baseUtcOffset); // Do not rename (binary serialization)
- info.AddValue("AdjustmentRules", _adjustmentRules); // Do not rename (binary serialization)
- info.AddValue("SupportsDaylightSavingTime", _supportsDaylightSavingTime); // Do not rename (binary serialization)
- }
-
- private TimeZoneInfo(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- _id = (String)info.GetValue("Id", typeof(String)); // Do not rename (binary serialization)
- _displayName = (String)info.GetValue("DisplayName", typeof(String)); // Do not rename (binary serialization)
- _standardDisplayName = (String)info.GetValue("StandardName", typeof(String)); // Do not rename (binary serialization)
- _daylightDisplayName = (String)info.GetValue("DaylightName", typeof(String)); // Do not rename (binary serialization)
- _baseUtcOffset = (TimeSpan)info.GetValue("BaseUtcOffset", typeof(TimeSpan)); // Do not rename (binary serialization)
- _adjustmentRules = (AdjustmentRule[])info.GetValue("AdjustmentRules", typeof(AdjustmentRule[])); // Do not rename (binary serialization)
- _supportsDaylightSavingTime = (Boolean)info.GetValue("SupportsDaylightSavingTime", typeof(Boolean)); // Do not rename (binary serialization)
- }
-
- internal class TimeZoneInformation
- {
- public string StandardName;
- public string DaylightName;
- public string TimeZoneKeyName;
-
- // we need to keep this one for subsequent interops.
- public TIME_DYNAMIC_ZONE_INFORMATION Dtzi;
-
- public unsafe TimeZoneInformation(TIME_DYNAMIC_ZONE_INFORMATION dtzi)
- {
- StandardName = new String(dtzi.StandardName);
- DaylightName = new String(dtzi.DaylightName);
- TimeZoneKeyName = new String(dtzi.TimeZoneKeyName);
- Dtzi = dtzi;
- }
- }
-
- //
- // TryGetTimeZone -
- //
- // Helper function for retrieving a TimeZoneInfo object by <time_zone_name>.
- //
- // This function may return null.
- //
- // assumes cachedData lock is taken
- //
- private static TimeZoneInfoResult TryGetTimeZone(ref TimeZoneInformation timeZoneInformation, Boolean dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData)
- {
- TimeZoneInfoResult result = TimeZoneInfoResult.Success;
- e = null;
- TimeZoneInfo match = null;
-
- // check the cache
- if (cachedData._systemTimeZones != null)
- {
- if (cachedData._systemTimeZones.TryGetValue(timeZoneInformation.TimeZoneKeyName, out match))
- {
- if (dstDisabled && match._supportsDaylightSavingTime)
- {
- // we found a cache hit but we want a time zone without DST and this one has DST data
- value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
- }
- else
- {
- value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
- match._daylightDisplayName, match._adjustmentRules, false);
- }
- return result;
- }
- }
-
- // fall back to reading from the local machine
- // when the cache is not fully populated
- result = TryGetFullTimeZoneInformation(timeZoneInformation, out match, out e, timeZoneInformation.Dtzi.Bias);
-
- if (result == TimeZoneInfoResult.Success)
- {
- if (cachedData._systemTimeZones == null)
- cachedData._systemTimeZones = new LowLevelDictionaryWithIEnumerable<CachedData.OrdinalIgnoreCaseString, TimeZoneInfo>();
-
- cachedData._systemTimeZones.Add(timeZoneInformation.TimeZoneKeyName, match);
-
- if (dstDisabled && match._supportsDaylightSavingTime)
- {
- // we found a cache hit but we want a time zone without DST and this one has DST data
- value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName);
- }
- else
- {
- value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
- match._daylightDisplayName, match._adjustmentRules, false);
- }
- }
- else
- {
- value = null;
- }
-
- return result;
- }
-
- //
- // GetLocalTimeZoneFromWin32Data -
- //
- // Helper function used by 'GetLocalTimeZone()' - this function wraps a bunch of
- // try/catch logic for handling the TimeZoneInfo private constructor that takes
- // a Win32Native.TimeZoneInformation structure.
- //
- private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(TimeZoneInformation timeZoneInformation, Boolean dstDisabled)
- {
- // first try to create the TimeZoneInfo with the original 'dstDisabled' flag
- try
- {
- return new TimeZoneInfo(timeZoneInformation, dstDisabled);
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
-
- // if 'dstDisabled' was false then try passing in 'true' as a last ditch effort
- if (!dstDisabled)
- {
- try
- {
- return new TimeZoneInfo(timeZoneInformation, true);
- }
- catch (ArgumentException) { }
- catch (InvalidTimeZoneException) { }
- }
-
- // the data returned from Windows is completely bogus; return a dummy entry
- return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);
- }
-
- internal static TimeZoneInfoResult TryGetFullTimeZoneInformation(TimeZoneInformation timeZoneInformation, out TimeZoneInfo value, out Exception e, int defaultBaseUtcOffset)
- {
- uint firstYear, lastYear;
- AdjustmentRule rule;
- AdjustmentRule[] zoneRules = null;
-
- value = null;
- e = null;
-
- //
- // First get the adjustment rules
- //
-
- if (Interop.mincore.GetDynamicTimeZoneInformationEffectiveYears(ref timeZoneInformation.Dtzi, out firstYear, out lastYear) != 0)
- {
- rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
- if (rule != null)
- {
- zoneRules = new AdjustmentRule[1] { rule };
- }
- }
- else
- {
- if (firstYear == lastYear)
- {
- // there is just 1 dynamic rule for this time zone.
- rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
- if (rule != null)
- {
- zoneRules = new AdjustmentRule[1] { rule };
- }
- }
- else
- {
- TIME_ZONE_INFORMATION tzdi = new TIME_ZONE_INFORMATION();
- LowLevelList<AdjustmentRule> rules = new LowLevelList<AdjustmentRule>();
-
- //
- // First rule
- //
-
- if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)firstYear, ref timeZoneInformation.Dtzi, out tzdi))
- {
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, DateTime.MinValue.Date, new DateTime((int)firstYear, 12, 31), defaultBaseUtcOffset);
- if (rule != null)
- {
- rules.Add(rule);
- }
-
- for (uint i = firstYear + 1; i < lastYear; i++)
- {
- if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)i, ref timeZoneInformation.Dtzi, out tzdi))
- {
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)i, 1, 1), new DateTime((int)i, 12, 31), defaultBaseUtcOffset);
- if (rule != null)
- {
- rules.Add(rule);
- }
- }
-
- //
- // Last rule
- //
-
- if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)lastYear, ref timeZoneInformation.Dtzi, out tzdi))
- {
- return TimeZoneInfoResult.InvalidTimeZoneException;
- }
- rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset);
- if (rule != null)
- {
- rules.Add(rule);
- }
-
- if (rules.Count > 0)
- {
- zoneRules = rules.ToArray();
- }
- }
- }
-
- //
- // Create TimeZoneInfo object
- //
- try
- {
- // Note that all names we have are localized names as Windows always return the localized names
- value = new TimeZoneInfo(
- timeZoneInformation.TimeZoneKeyName,
- new TimeSpan(0, -(timeZoneInformation.Dtzi.Bias), 0),
- timeZoneInformation.StandardName, // we use the display name as the standared names
- timeZoneInformation.StandardName,
- timeZoneInformation.DaylightName,
- zoneRules,
- false);
-
- return System.TimeZoneInfo.TimeZoneInfoResult.Success;
- }
- catch (ArgumentException ex)
- {
- // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
- value = null;
- e = ex;
- return System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException;
- }
- catch (InvalidTimeZoneException ex)
- {
- // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException
- value = null;
- e = ex;
- return System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException;
- }
- }
-
- // -------- SECTION: constructors -----------------*
- //
- // TimeZoneInfo -
- //
- // private ctor
- //
- private TimeZoneInfo(TimeZoneInformation zone, Boolean dstDisabled)
- {
- if (String.IsNullOrEmpty(zone.StandardName))
- {
- _id = c_localId; // the ID must contain at least 1 character - initialize m_id to "Local"
- }
- else
- {
- _id = zone.StandardName;
- }
- _baseUtcOffset = new TimeSpan(0, -(zone.Dtzi.Bias), 0);
-
- if (!dstDisabled)
- {
- // only create the adjustment rule if DST is enabled
- AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(zone, DateTime.MinValue.Date, DateTime.MaxValue.Date, zone.Dtzi.Bias);
- if (rule != null)
- {
- _adjustmentRules = new AdjustmentRule[1];
- _adjustmentRules[0] = rule;
- }
- }
-
- ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
- _displayName = zone.StandardName;
- _standardDisplayName = zone.StandardName;
- _daylightDisplayName = zone.DaylightName;
- }
-
- private TimeZoneInfo(
- String id,
- TimeSpan baseUtcOffset,
- String displayName,
- String standardDisplayName,
- String daylightDisplayName,
- AdjustmentRule[] adjustmentRules,
- Boolean disableDaylightSavingTime)
- {
- Boolean adjustmentRulesSupportDst;
- ValidateTimeZoneInfo(id, baseUtcOffset, adjustmentRules, out adjustmentRulesSupportDst);
-
- _id = id;
- _baseUtcOffset = baseUtcOffset;
- _displayName = displayName;
- _standardDisplayName = standardDisplayName;
- _daylightDisplayName = (disableDaylightSavingTime ? null : daylightDisplayName);
- _supportsDaylightSavingTime = adjustmentRulesSupportDst && !disableDaylightSavingTime;
- _adjustmentRules = adjustmentRules;
- }
-
- //
- // CreateAdjustmentRuleFromTimeZoneInformation-
- //
- // Converts TimeZoneInformation to an AdjustmentRule
- //
- internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(TimeZoneInformation timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
- {
- bool supportsDst = (timeZoneInformation.Dtzi.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- if (timeZoneInformation.Dtzi.Bias == defaultBaseUtcOffset)
- {
- // this rule will not contain any information to be used to adjust dates. just ignore it
- return null;
- }
-
- return AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- TimeSpan.Zero, // no daylight saving transition
- TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
- TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Dtzi.Bias, 0)); // Bias delta is all what we need from this rule
- }
-
- //
- // Create an AdjustmentRule with TransitionTime objects
- //
- TransitionTime daylightTransitionStart;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */))
- {
- return null;
- }
-
- TransitionTime daylightTransitionEnd;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */))
- {
- return null;
- }
-
- if (daylightTransitionStart.Equals(daylightTransitionEnd))
- {
- // this happens when the time zone does support DST but the OS has DST disabled
- return null;
- }
-
- return AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- new TimeSpan(0, -timeZoneInformation.Dtzi.DaylightBias, 0),
- (TransitionTime)daylightTransitionStart,
- (TransitionTime)daylightTransitionEnd,
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Dtzi.Bias, 0));
- }
-
- internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(ref TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
- {
- bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- if (timeZoneInformation.Bias == defaultBaseUtcOffset)
- {
- // this rule will not contain any information to be used to adjust dates. just ignore it
- return null;
- }
-
- return AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- TimeSpan.Zero, // no daylight saving transition
- TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1),
- TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1),
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0)); // Bias delta is all what we need from this rule
- }
-
- //
- // Create an AdjustmentRule with TransitionTime objects
- //
- TransitionTime daylightTransitionStart;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */))
- {
- return null;
- }
-
- TransitionTime daylightTransitionEnd;
- if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */))
- {
- return null;
- }
-
- if (daylightTransitionStart.Equals(daylightTransitionEnd))
- {
- // this happens when the time zone does support DST but the OS has DST disabled
- return null;
- }
-
- return AdjustmentRule.CreateAdjustmentRule(
- startDate,
- endDate,
- new TimeSpan(0, -timeZoneInformation.DaylightBias, 0),
- (TransitionTime)daylightTransitionStart,
- (TransitionTime)daylightTransitionEnd,
- new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0));
- }
-
- //
- // Overloaded method which take TimeZoneInformation
- //
-
- private static bool TransitionTimeFromTimeZoneInformation(TimeZoneInformation timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
- {
- bool supportsDst = (timeZoneInformation.Dtzi.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- transitionTime = default(TransitionTime);
- return false;
- }
-
- if (readStartDate)
- {
- //
- // read the "daylightTransitionStart"
- //
- if (timeZoneInformation.Dtzi.DaylightDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.Dtzi.DaylightDate.wHour,
- timeZoneInformation.Dtzi.DaylightDate.wMinute,
- timeZoneInformation.Dtzi.DaylightDate.wSecond,
- timeZoneInformation.Dtzi.DaylightDate.wMilliseconds),
- timeZoneInformation.Dtzi.DaylightDate.wMonth,
- timeZoneInformation.Dtzi.DaylightDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.Dtzi.DaylightDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.Dtzi.DaylightDate.wHour,
- timeZoneInformation.Dtzi.DaylightDate.wMinute,
- timeZoneInformation.Dtzi.DaylightDate.wSecond,
- timeZoneInformation.Dtzi.DaylightDate.wMilliseconds),
- timeZoneInformation.Dtzi.DaylightDate.wMonth,
- timeZoneInformation.Dtzi.DaylightDate.wDay);
- }
- }
- else
- {
- //
- // read the "daylightTransitionEnd"
- //
- if (timeZoneInformation.Dtzi.StandardDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.Dtzi.StandardDate.wHour,
- timeZoneInformation.Dtzi.StandardDate.wMinute,
- timeZoneInformation.Dtzi.StandardDate.wSecond,
- timeZoneInformation.Dtzi.StandardDate.wMilliseconds),
- timeZoneInformation.Dtzi.StandardDate.wMonth,
- timeZoneInformation.Dtzi.StandardDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.Dtzi.StandardDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.Dtzi.StandardDate.wHour,
- timeZoneInformation.Dtzi.StandardDate.wMinute,
- timeZoneInformation.Dtzi.StandardDate.wSecond,
- timeZoneInformation.Dtzi.StandardDate.wMilliseconds),
- timeZoneInformation.Dtzi.StandardDate.wMonth,
- timeZoneInformation.Dtzi.StandardDate.wDay);
- }
- }
-
- return true;
- }
-
- //
- // TransitionTimeFromTimeZoneInformation -
- //
- // Converts a TimeZoneInformation (REG_TZI_FORMAT struct) to a TransitionTime
- //
- // * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
- // * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
- //
- private static bool TransitionTimeFromTimeZoneInformation(TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
- {
- //
- // SYSTEMTIME -
- //
- // If the time zone does not support daylight saving time or if the caller needs
- // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
- // must be zero. If this date is specified, the DaylightDate value in the
- // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system
- // assumes the time zone data is invalid and no changes will be applied.
- //
- bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0);
-
- if (!supportsDst)
- {
- transitionTime = default(TransitionTime);
- return false;
- }
-
- //
- // SYSTEMTIME -
- //
- // * FixedDateRule -
- // If the Year member is not zero, the transition date is absolute; it will only occur one time
- //
- // * FloatingDateRule -
- // To select the correct day in the month, set the Year member to zero, the Hour and Minute
- // members to the transition time, the DayOfWeek member to the appropriate weekday, and the
- // Day member to indicate the occurence of the day of the week within the month (first through fifth).
- //
- // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows:
- // Hour = 2,
- // Month = 4,
- // DayOfWeek = 0,
- // Day = 1.
- //
- // Specify 2:00a.m. on the last Thursday in October as follows:
- // Hour = 2,
- // Month = 10,
- // DayOfWeek = 4,
- // Day = 5.
- //
- if (readStartDate)
- {
- //
- // read the "daylightTransitionStart"
- //
- if (timeZoneInformation.DaylightDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.DaylightDate.wHour,
- timeZoneInformation.DaylightDate.wMinute,
- timeZoneInformation.DaylightDate.wSecond,
- timeZoneInformation.DaylightDate.wMilliseconds),
- timeZoneInformation.DaylightDate.wMonth,
- timeZoneInformation.DaylightDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.DaylightDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.DaylightDate.wHour,
- timeZoneInformation.DaylightDate.wMinute,
- timeZoneInformation.DaylightDate.wSecond,
- timeZoneInformation.DaylightDate.wMilliseconds),
- timeZoneInformation.DaylightDate.wMonth,
- timeZoneInformation.DaylightDate.wDay);
- }
- }
- else
- {
- //
- // read the "daylightTransitionEnd"
- //
- if (timeZoneInformation.StandardDate.wYear == 0)
- {
- transitionTime = TransitionTime.CreateFloatingDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.StandardDate.wHour,
- timeZoneInformation.StandardDate.wMinute,
- timeZoneInformation.StandardDate.wSecond,
- timeZoneInformation.StandardDate.wMilliseconds),
- timeZoneInformation.StandardDate.wMonth,
- timeZoneInformation.StandardDate.wDay, /* Week 1-5 */
- (DayOfWeek)timeZoneInformation.StandardDate.wDayOfWeek);
- }
- else
- {
- transitionTime = TransitionTime.CreateFixedDateRule(
- new DateTime(1, /* year */
- 1, /* month */
- 1, /* day */
- timeZoneInformation.StandardDate.wHour,
- timeZoneInformation.StandardDate.wMinute,
- timeZoneInformation.StandardDate.wSecond,
- timeZoneInformation.StandardDate.wMilliseconds),
- timeZoneInformation.StandardDate.wMonth,
- timeZoneInformation.StandardDate.wDay);
- }
- }
-
- return true;
- }
- /*============================================================
- **
- ** Class: TimeZoneInfo.AdjustmentRule
- **
- **
- ** Purpose:
- ** This class is used to represent a Dynamic TimeZone. It
- ** has methods for converting a DateTime to UTC from local time
- ** and to local time from UTC and methods for getting the
- ** standard name and daylight name of the time zone.
- **
- **
- ============================================================*/
- [Serializable]
- sealed public class AdjustmentRule : IEquatable<AdjustmentRule>, ISerializable, IDeserializationCallback
- {
- // ---- SECTION: members supporting exposed properties -------------*
- private readonly DateTime _dateStart;
- private readonly DateTime _dateEnd;
- private readonly TimeSpan _daylightDelta;
- private readonly TransitionTime _daylightTransitionStart;
- private readonly TransitionTime _daylightTransitionEnd;
- private readonly TimeSpan _baseUtcOffsetDelta; // delta from the default Utc offset (utcOffset = defaultUtcOffset + _baseUtcOffsetDelta)
-
-
- // ---- SECTION: public properties --------------*
- public DateTime DateStart
- {
- get
- {
- return _dateStart;
- }
- }
-
- public DateTime DateEnd
- {
- get
- {
- return _dateEnd;
- }
- }
-
- public TimeSpan DaylightDelta
- {
- get
- {
- return _daylightDelta;
- }
- }
-
-
- public TransitionTime DaylightTransitionStart
- {
- get
- {
- return _daylightTransitionStart;
- }
- }
-
-
- public TransitionTime DaylightTransitionEnd
- {
- get
- {
- return _daylightTransitionEnd;
- }
- }
-
- internal TimeSpan BaseUtcOffsetDelta
- {
- get
- {
- return _baseUtcOffsetDelta;
- }
- }
-
- internal bool HasDaylightSaving
- {
- get
- {
- return this.DaylightDelta != TimeSpan.Zero ||
- this.DaylightTransitionStart.TimeOfDay != DateTime.MinValue ||
- this.DaylightTransitionEnd.TimeOfDay != DateTime.MinValue.AddMilliseconds(1);
- }
- }
-
- // ---- SECTION: public methods --------------*
-
- // IEquatable<AdjustmentRule>
- public bool Equals(AdjustmentRule other)
- {
- return other != null
- && _dateStart == other._dateStart
- && _dateEnd == other._dateEnd
- && _daylightDelta == other._daylightDelta
- && _baseUtcOffsetDelta == other._baseUtcOffsetDelta
- && _daylightTransitionEnd.Equals(other._daylightTransitionEnd)
- && _daylightTransitionStart.Equals(other._daylightTransitionStart);
- }
-
-
- public override int GetHashCode()
- {
- return _dateStart.GetHashCode();
- }
-
-
-
- // -------- SECTION: constructors -----------------*
-
- private AdjustmentRule(
- DateTime dateStart,
- DateTime dateEnd,
- TimeSpan daylightDelta,
- TransitionTime daylightTransitionStart,
- TransitionTime daylightTransitionEnd,
- TimeSpan baseUtcOffsetDelta)
- {
- ValidateAdjustmentRule(dateStart, dateEnd, daylightDelta,
- daylightTransitionStart, daylightTransitionEnd);
-
- _dateStart = dateStart;
- _dateEnd = dateEnd;
- _daylightDelta = daylightDelta;
- _daylightTransitionStart = daylightTransitionStart;
- _daylightTransitionEnd = daylightTransitionEnd;
- _baseUtcOffsetDelta = baseUtcOffsetDelta;
- }
-
-
- // -------- SECTION: factory methods -----------------*
-
- public static AdjustmentRule CreateAdjustmentRule(
- DateTime dateStart,
- DateTime dateEnd,
- TimeSpan daylightDelta,
- TransitionTime daylightTransitionStart,
- TransitionTime daylightTransitionEnd)
- {
- return new AdjustmentRule(
- dateStart,
- dateEnd,
- daylightDelta,
- daylightTransitionStart,
- daylightTransitionEnd,
- baseUtcOffsetDelta: TimeSpan.Zero);
- }
-
- internal static AdjustmentRule CreateAdjustmentRule(
- DateTime dateStart,
- DateTime dateEnd,
- TimeSpan daylightDelta,
- TransitionTime daylightTransitionStart,
- TransitionTime daylightTransitionEnd,
- TimeSpan baseUtcOffsetDelta)
- {
- return new AdjustmentRule(
- dateStart,
- dateEnd,
- daylightDelta,
- daylightTransitionStart,
- daylightTransitionEnd,
- baseUtcOffsetDelta);
- }
-
- // ----- SECTION: internal utility methods ----------------*
-
- //
- // When Windows sets the daylight transition start Jan 1st at 12:00 AM, it means the year starts with the daylight saving on.
- // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
- //
- internal bool IsStartDateMarkerForBeginningOfYear()
- {
- return DaylightTransitionStart.Month == 1 && DaylightTransitionStart.Day == 1 && DaylightTransitionStart.TimeOfDay.Hour == 0 &&
- DaylightTransitionStart.TimeOfDay.Minute == 0 && DaylightTransitionStart.TimeOfDay.Second == 0 &&
- _dateStart.Year == _dateEnd.Year;
- }
-
- //
- // When Windows sets the daylight transition end Jan 1st at 12:00 AM, it means the year ends with the daylight saving on.
- // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
- //
- internal bool IsEndDateMarkerForEndOfYear()
- {
- return DaylightTransitionEnd.Month == 1 && DaylightTransitionEnd.Day == 1 && DaylightTransitionEnd.TimeOfDay.Hour == 0 &&
- DaylightTransitionEnd.TimeOfDay.Minute == 0 && DaylightTransitionEnd.TimeOfDay.Second == 0 &&
- _dateStart.Year == _dateEnd.Year;
- }
-
- //
- // ValidateAdjustmentRule -
- //
- // Helper function that performs all of the validation checks for the
- // factory methods and deserialization callback
- //
- private static void ValidateAdjustmentRule(
- DateTime dateStart,
- DateTime dateEnd,
- TimeSpan daylightDelta,
- TransitionTime daylightTransitionStart,
- TransitionTime daylightTransitionEnd)
- {
- if (dateStart.Kind != DateTimeKind.Unspecified)
- {
- throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecified, nameof(dateStart));
- }
-
- if (dateEnd.Kind != DateTimeKind.Unspecified)
- {
- throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecified, nameof(dateEnd));
- }
-
- if (daylightTransitionStart.Equals(daylightTransitionEnd))
- {
- throw new ArgumentException(SR.Argument_TransitionTimesAreIdentical,
- nameof(daylightTransitionEnd));
- }
-
-
- if (dateStart > dateEnd)
- {
- throw new ArgumentException(SR.Argument_OutOfOrderDateTimes, nameof(dateStart));
- }
-
- if (TimeZoneInfo.UtcOffsetOutOfRange(daylightDelta))
- {
- throw new ArgumentOutOfRangeException(nameof(daylightDelta), daylightDelta,
- SR.ArgumentOutOfRange_UtcOffset);
- }
-
- if (daylightDelta.Ticks % TimeSpan.TicksPerMinute != 0)
- {
- throw new ArgumentException(SR.Argument_TimeSpanHasSeconds,
- nameof(daylightDelta));
- }
-
- if (dateStart.TimeOfDay != TimeSpan.Zero)
- {
- throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay,
- nameof(dateStart));
- }
-
- if (dateEnd.TimeOfDay != TimeSpan.Zero)
- {
- throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay,
- nameof(dateEnd));
- }
- }
-
-
-
- // ----- SECTION: private serialization instance methods ----------------*
-
- void IDeserializationCallback.OnDeserialization(Object sender)
- {
- // OnDeserialization is called after each instance of this class is deserialized.
- // This callback method performs AdjustmentRule validation after being deserialized.
-
- try
- {
- ValidateAdjustmentRule(_dateStart, _dateEnd, _daylightDelta,
- _daylightTransitionStart, _daylightTransitionEnd);
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
-
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- info.AddValue("DateStart", _dateStart); // Do not rename (binary serialization)
- info.AddValue("DateEnd", _dateEnd); // Do not rename (binary serialization)
- info.AddValue("DaylightDelta", _daylightDelta); // Do not rename (binary serialization)
- info.AddValue("DaylightTransitionStart", _daylightTransitionStart); // Do not rename (binary serialization)
- info.AddValue("DaylightTransitionEnd", _daylightTransitionEnd); // Do not rename (binary serialization)
- info.AddValue("BaseUtcOffsetDelta", _baseUtcOffsetDelta); // Do not rename (binary serialization)
- }
-
- private AdjustmentRule(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- _dateStart = (DateTime)info.GetValue("DateStart", typeof(DateTime)); // Do not rename (binary serialization)
- _dateEnd = (DateTime)info.GetValue("DateEnd", typeof(DateTime)); // Do not rename (binary serialization)
- _daylightDelta = (TimeSpan)info.GetValue("DaylightDelta", typeof(TimeSpan)); // Do not rename (binary serialization)
- _daylightTransitionStart = (TransitionTime)info.GetValue("DaylightTransitionStart", typeof(TransitionTime)); // Do not rename (binary serialization)
- _daylightTransitionEnd = (TransitionTime)info.GetValue("DaylightTransitionEnd", typeof(TransitionTime)); // Do not rename (binary serialization)
-
- object o = info.GetValueNoThrow("BaseUtcOffsetDelta", typeof(TimeSpan)); // Do not rename (binary serialization)
- if (o != null)
- {
- _baseUtcOffsetDelta = (TimeSpan)o;
- }
- }
- }
-
-
- /*============================================================
- **
- ** Class: TimeZoneInfo.TransitionTime
- **
- **
- ** Purpose:
- ** This class is used to represent a Dynamic TimeZone. It
- ** has methods for converting a DateTime to UTC from local time
- ** and to local time from UTC and methods for getting the
- ** standard name and daylight name of the time zone.
- **
- **
- ============================================================*/
- [Serializable]
- public readonly struct TransitionTime : IEquatable<TransitionTime>, ISerializable, IDeserializationCallback
- {
- // ---- SECTION: members supporting exposed properties -------------*
- private readonly DateTime _timeOfDay;
- private readonly byte _month;
- private readonly byte _week;
- private readonly byte _day;
- private readonly DayOfWeek _dayOfWeek;
- private readonly Boolean _isFixedDateRule;
-
-
- // ---- SECTION: public properties --------------*
- public DateTime TimeOfDay
- {
- get
- {
- return _timeOfDay;
- }
- }
-
- public Int32 Month
- {
- get
- {
- return (int)_month;
- }
- }
-
-
- public Int32 Week
- {
- get
- {
- return (int)_week;
- }
- }
-
- public Int32 Day
- {
- get
- {
- return (int)_day;
- }
- }
-
- public DayOfWeek DayOfWeek
- {
- get
- {
- return _dayOfWeek;
- }
- }
-
- public Boolean IsFixedDateRule
- {
- get
- {
- return _isFixedDateRule;
- }
- }
-
- // ---- SECTION: public methods --------------*
- public override bool Equals(Object obj)
- {
- if (obj is TransitionTime)
- {
- return Equals((TransitionTime)obj);
- }
- return false;
- }
-
- public static bool operator ==(TransitionTime t1, TransitionTime t2)
- {
- return t1.Equals(t2);
- }
-
- public static bool operator !=(TransitionTime t1, TransitionTime t2)
- {
- return (!t1.Equals(t2));
- }
-
- public bool Equals(TransitionTime other)
- {
- bool equal = (_isFixedDateRule == other._isFixedDateRule
- && _timeOfDay == other._timeOfDay
- && _month == other._month);
-
- if (equal)
- {
- if (other._isFixedDateRule)
- {
- equal = (_day == other._day);
- }
- else
- {
- equal = (_week == other._week
- && _dayOfWeek == other._dayOfWeek);
- }
- }
- return equal;
- }
-
-
- public override int GetHashCode()
- {
- return ((int)_month ^ (int)_week << 8);
- }
-
-
- // -------- SECTION: constructors -----------------*
-
- private TransitionTime(
- DateTime timeOfDay,
- Int32 month,
- Int32 week,
- Int32 day,
- DayOfWeek dayOfWeek,
- Boolean isFixedDateRule)
- {
- ValidateTransitionTime(timeOfDay, month, week, day, dayOfWeek);
-
- _timeOfDay = timeOfDay;
- _month = (byte)month;
- _week = (byte)week;
- _day = (byte)day;
- _dayOfWeek = dayOfWeek;
- _isFixedDateRule = isFixedDateRule;
- }
-
-
- // -------- SECTION: factory methods -----------------*
-
-
- public static TransitionTime CreateFixedDateRule(
- DateTime timeOfDay,
- Int32 month,
- Int32 day)
- {
- return new TransitionTime(timeOfDay, month, 1, day, DayOfWeek.Sunday, true);
- }
-
-
- public static TransitionTime CreateFloatingDateRule(
- DateTime timeOfDay,
- Int32 month,
- Int32 week,
- DayOfWeek dayOfWeek)
- {
- return new TransitionTime(timeOfDay, month, week, 1, dayOfWeek, false);
- }
-
-
- // ----- SECTION: internal utility methods ----------------*
-
- //
- // ValidateTransitionTime -
- //
- // Helper function that validates a TransitionTime instance
- //
- private static void ValidateTransitionTime(
- DateTime timeOfDay,
- Int32 month,
- Int32 week,
- Int32 day,
- DayOfWeek dayOfWeek)
- {
- if (timeOfDay.Kind != DateTimeKind.Unspecified)
- {
- throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecified, nameof(timeOfDay));
- }
-
- // Month range 1-12
- if (month < 1 || month > 12)
- {
- throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_MonthParam);
- }
-
- // Day range 1-31
- if (day < 1 || day > 31)
- {
- throw new ArgumentOutOfRangeException(nameof(day), SR.ArgumentOutOfRange_DayParam);
- }
-
- // Week range 1-5
- if (week < 1 || week > 5)
- {
- throw new ArgumentOutOfRangeException(nameof(week), SR.ArgumentOutOfRange_Week);
- }
-
- // DayOfWeek range 0-6
- if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6)
- {
- throw new ArgumentOutOfRangeException(nameof(dayOfWeek), SR.ArgumentOutOfRange_DayOfWeek);
- }
-
- if (timeOfDay.Year != 1 || timeOfDay.Month != 1
- || timeOfDay.Day != 1 || (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0))
- {
- throw new ArgumentException(SR.Argument_DateTimeHasTicks, nameof(timeOfDay));
- }
- }
-
- void IDeserializationCallback.OnDeserialization(Object sender)
- {
- // OnDeserialization is called after each instance of this class is deserialized.
- // This callback method performs TransitionTime validation after being deserialized.
-
- try
- {
- ValidateTransitionTime(_timeOfDay, (Int32)_month, (Int32)_week, (Int32)_day, _dayOfWeek);
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
-
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- info.AddValue("TimeOfDay", _timeOfDay); // Do not rename (binary serialization)
- info.AddValue("Month", _month); // Do not rename (binary serialization)
- info.AddValue("Week", _week); // Do not rename (binary serialization)
- info.AddValue("Day", _day); // Do not rename (binary serialization)
- info.AddValue("DayOfWeek", _dayOfWeek); // Do not rename (binary serialization)
- info.AddValue("IsFixedDateRule", _isFixedDateRule); // Do not rename (binary serialization)
- }
-
- private TransitionTime(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- _timeOfDay = (DateTime)info.GetValue("TimeOfDay", typeof(DateTime)); // Do not rename (binary serialization)
- _month = (byte)info.GetValue("Month", typeof(byte)); // Do not rename (binary serialization)
- _week = (byte)info.GetValue("Week", typeof(byte)); // Do not rename (binary serialization)
- _day = (byte)info.GetValue("Day", typeof(byte)); // Do not rename (binary serialization)
- _dayOfWeek = (DayOfWeek)info.GetValue("DayOfWeek", typeof(DayOfWeek)); // Do not rename (binary serialization)
- _isFixedDateRule = (Boolean)info.GetValue("IsFixedDateRule", typeof(Boolean)); // Do not rename (binary serialization)
- }
- }
-
-
- /*============================================================
- **
- ** Class: TimeZoneInfo.StringSerializer
- **
- **
- ** Purpose:
- ** This class is used to serialize and deserialize TimeZoneInfo
- ** objects based on the custom string serialization format
- **
- **
- ============================================================*/
- sealed private class StringSerializer
- {
- // ---- SECTION: private members -------------*
- private enum State
- {
- Escaped = 0,
- NotEscaped = 1,
- StartOfToken = 2,
- EndOfLine = 3
- }
-
- private String _serializedText;
- private int _currentTokenStartIndex;
- private State _state;
-
- // the majority of the strings contained in the OS time zones fit in 64 chars
- private const int initialCapacityForString = 64;
- private const char esc = '\\';
- private const char sep = ';';
- private const char lhs = '[';
- private const char rhs = ']';
- private const string escString = "\\";
- private const string sepString = ";";
- private const string lhsString = "[";
- private const string rhsString = "]";
- private const string escapedEsc = "\\\\";
- private const string escapedSep = "\\;";
- private const string escapedLhs = "\\[";
- private const string escapedRhs = "\\]";
- private const string dateTimeFormat = "MM:dd:yyyy";
- private const string timeOfDayFormat = "HH:mm:ss.FFF";
-
-
- // ---- SECTION: public static methods --------------*
-
- //
- // GetSerializedString -
- //
- // static method that creates the custom serialized string
- // representation of a TimeZoneInfo instance
- //
- public static String GetSerializedString(TimeZoneInfo zone)
- {
- StringBuilder serializedText = StringBuilderCache.Acquire();
-
- //
- // <_id>;<_baseUtcOffset>;<_displayName>;<_standardDisplayName>;<_daylightDispayName>
- //
- serializedText.Append(SerializeSubstitute(zone.Id));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(
- zone.BaseUtcOffset.TotalMinutes.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(zone.DisplayName));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(zone.StandardName));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(zone.DaylightName));
- serializedText.Append(sep);
-
- AdjustmentRule[] rules = zone.GetAdjustmentRules();
-
- if (rules != null && rules.Length > 0)
- {
- for (int i = 0; i < rules.Length; i++)
- {
- AdjustmentRule rule = rules[i];
-
- serializedText.Append(lhs);
- serializedText.Append(SerializeSubstitute(rule.DateStart.ToString(
- dateTimeFormat, DateTimeFormatInfo.InvariantInfo)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(rule.DateEnd.ToString(
- dateTimeFormat, DateTimeFormatInfo.InvariantInfo)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(rule.DaylightDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- // serialize the TransitionTime's
- SerializeTransitionTime(rule.DaylightTransitionStart, serializedText);
- serializedText.Append(sep);
- SerializeTransitionTime(rule.DaylightTransitionEnd, serializedText);
- serializedText.Append(sep);
- if (rule.BaseUtcOffsetDelta != TimeSpan.Zero)
- { // Serialize it only when BaseUtcOffsetDelta has a value to reduce the impact of adding rule.BaseUtcOffsetDelta
- serializedText.Append(SerializeSubstitute(rule.BaseUtcOffsetDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- }
- serializedText.Append(rhs);
- }
- }
- serializedText.Append(sep);
- return StringBuilderCache.GetStringAndRelease(serializedText);
- }
-
-
- //
- // GetDeserializedTimeZoneInfo -
- //
- // static method that instantiates a TimeZoneInfo from a custom serialized
- // string
- //
- public static TimeZoneInfo GetDeserializedTimeZoneInfo(String source)
- {
- StringSerializer s = new StringSerializer(source);
-
- String id = s.GetNextStringValue(false);
- TimeSpan baseUtcOffset = s.GetNextTimeSpanValue(false);
- String displayName = s.GetNextStringValue(false);
- String standardName = s.GetNextStringValue(false);
- String daylightName = s.GetNextStringValue(false);
- AdjustmentRule[] rules = s.GetNextAdjustmentRuleArrayValue(false);
-
- try
- {
- return new TimeZoneInfo(id, baseUtcOffset, displayName, standardName, daylightName, rules, disableDaylightSavingTime: false);
- }
- catch (ArgumentException ex)
- {
- throw new SerializationException(SR.Serialization_InvalidData, ex);
- }
- catch (InvalidTimeZoneException ex)
- {
- throw new SerializationException(SR.Serialization_InvalidData, ex);
- }
- }
-
- // ---- SECTION: public instance methods --------------*
-
-
- // -------- SECTION: constructors -----------------*
-
- //
- // StringSerializer -
- //
- // private constructor - used by GetDeserializedTimeZoneInfo()
- //
- private StringSerializer(String str)
- {
- _serializedText = str;
- _state = State.StartOfToken;
- }
-
-
-
- // ----- SECTION: internal static utility methods ----------------*
-
- //
- // SerializeSubstitute -
- //
- // returns a new string with all of the reserved sub-strings escaped
- //
- // ";" -> "\;"
- // "[" -> "\["
- // "]" -> "\]"
- // "\" -> "\\"
- //
- private static String SerializeSubstitute(String text)
- {
- text = text.Replace(escString, escapedEsc);
- text = text.Replace(lhsString, escapedLhs);
- text = text.Replace(rhsString, escapedRhs);
- return text.Replace(sepString, escapedSep);
- }
-
-
- //
- // SerializeTransitionTime -
- //
- // Helper method to serialize a TimeZoneInfo.TransitionTime object
- //
- private static void SerializeTransitionTime(TransitionTime time, StringBuilder serializedText)
- {
- serializedText.Append(lhs);
- Int32 fixedDate = (time.IsFixedDateRule ? 1 : 0);
- serializedText.Append(fixedDate.ToString(CultureInfo.InvariantCulture));
- serializedText.Append(sep);
-
- if (time.IsFixedDateRule)
- {
- serializedText.Append(SerializeSubstitute(time.TimeOfDay.ToString(timeOfDayFormat, DateTimeFormatInfo.InvariantInfo)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(time.Month.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(time.Day.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- }
- else
- {
- serializedText.Append(SerializeSubstitute(time.TimeOfDay.ToString(timeOfDayFormat, DateTimeFormatInfo.InvariantInfo)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(time.Month.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(time.Week.ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- serializedText.Append(SerializeSubstitute(((int)time.DayOfWeek).ToString(CultureInfo.InvariantCulture)));
- serializedText.Append(sep);
- }
- serializedText.Append(rhs);
- }
-
- //
- // VerifyIsEscapableCharacter -
- //
- // Helper function to determine if the passed in string token is allowed to be preceeded by an escape sequence token
- //
- private static void VerifyIsEscapableCharacter(char c)
- {
- if (c != esc && c != sep && c != lhs && c != rhs)
- {
- throw new SerializationException(String.Format(SR.Serialization_InvalidEscapeSequence, c));
- }
- }
-
- // ----- SECTION: internal instance utility methods ----------------*
-
- //
- // SkipVersionNextDataFields -
- //
- // Helper function that reads past "v.Next" data fields. Receives a "depth" parameter indicating the
- // current relative nested bracket depth that _currentTokenStartIndex is at. The function ends
- // successfully when "depth" returns to zero (0).
- //
- //
- private void SkipVersionNextDataFields(Int32 depth /* starting depth in the nested brackets ('[', ']')*/)
- {
- if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- State tokenState = State.NotEscaped;
-
- // walk the serialized text, building up the token as we go...
- for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++)
- {
- if (tokenState == State.Escaped)
- {
- VerifyIsEscapableCharacter(_serializedText[i]);
- tokenState = State.NotEscaped;
- }
- else if (tokenState == State.NotEscaped)
- {
- switch (_serializedText[i])
- {
- case esc:
- tokenState = State.Escaped;
- break;
-
- case lhs:
- depth++;
- break;
- case rhs:
- depth--;
- if (depth == 0)
- {
- _currentTokenStartIndex = i + 1;
- if (_currentTokenStartIndex >= _serializedText.Length)
- {
- _state = State.EndOfLine;
- }
- else
- {
- _state = State.StartOfToken;
- }
- return;
- }
- break;
-
- case '\0':
- // invalid character
- throw new SerializationException(SR.Serialization_InvalidData);
-
- default:
- break;
- }
- }
- }
-
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
-
- //
- // GetNextStringValue -
- //
- // Helper function that reads a string token from the serialized text. The function
- // updates the _currentTokenStartIndex to point to the next token on exit. Also _state
- // is set to either State.StartOfToken or State.EndOfLine on exit.
- //
- // The function takes a parameter "canEndWithoutSeparator".
- //
- // * When set to 'false' the function requires the string token end with a ";".
- // * When set to 'true' the function requires that the string token end with either
- // ";", State.EndOfLine, or "]". In the case that "]" is the terminal case the
- // _currentTokenStartIndex is left pointing at index "]" to allow the caller to update
- // its depth logic.
- //
- private String GetNextStringValue(Boolean canEndWithoutSeparator)
- {
- // first verify the internal state of the object
- if (_state == State.EndOfLine)
- {
- if (canEndWithoutSeparator)
- {
- return null;
- }
- else
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- }
- if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- State tokenState = State.NotEscaped;
- StringBuilder token = StringBuilderCache.Acquire(initialCapacityForString);
-
- // walk the serialized text, building up the token as we go...
- for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++)
- {
- if (tokenState == State.Escaped)
- {
- VerifyIsEscapableCharacter(_serializedText[i]);
- token.Append(_serializedText[i]);
- tokenState = State.NotEscaped;
- }
- else if (tokenState == State.NotEscaped)
- {
- switch (_serializedText[i])
- {
- case esc:
- tokenState = State.Escaped;
- break;
-
- case lhs:
- // '[' is an unexpected character
- throw new SerializationException(SR.Serialization_InvalidData);
-
- case rhs:
- if (canEndWithoutSeparator)
- {
- // if ';' is not a required terminal then treat ']' as a terminal
- // leave _currentTokenStartIndex pointing to ']' so our callers can handle
- // this special case
- _currentTokenStartIndex = i;
- _state = State.StartOfToken;
- return token.ToString();
- }
- else
- {
- // ']' is an unexpected character
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- case sep:
- _currentTokenStartIndex = i + 1;
- if (_currentTokenStartIndex >= _serializedText.Length)
- {
- _state = State.EndOfLine;
- }
- else
- {
- _state = State.StartOfToken;
- }
- return StringBuilderCache.GetStringAndRelease(token);
-
- case '\0':
- // invalid character
- throw new SerializationException(SR.Serialization_InvalidData);
-
- default:
- token.Append(_serializedText[i]);
- break;
- }
- }
- }
- //
- // we are at the end of the line
- //
- if (tokenState == State.Escaped)
- {
- // we are at the end of the serialized text but we are in an escaped state
- throw new SerializationException(String.Format(SR.Serialization_InvalidEscapeSequence, String.Empty));
- }
-
- if (!canEndWithoutSeparator)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- _currentTokenStartIndex = _serializedText.Length;
- _state = State.EndOfLine;
- return StringBuilderCache.GetStringAndRelease(token);
- }
-
- //
- // GetNextDateTimeValue -
- //
- // Helper function to read a DateTime token. Takes a boolean "canEndWithoutSeparator"
- // and a "format" string.
- //
- private DateTime GetNextDateTimeValue(Boolean canEndWithoutSeparator, string format)
- {
- String token = GetNextStringValue(canEndWithoutSeparator);
- DateTime time;
- if (!DateTime.TryParseExact(token, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out time))
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- return time;
- }
-
- //
- // GetNextTimeSpanValue -
- //
- // Helper function to read a DateTime token. Takes a boolean "canEndWithoutSeparator".
- //
- private TimeSpan GetNextTimeSpanValue(Boolean canEndWithoutSeparator)
- {
- Int32 token = GetNextInt32Value(canEndWithoutSeparator);
-
- try
- {
- return new TimeSpan(0 /* hours */, token /* minutes */, 0 /* seconds */);
- }
- catch (ArgumentOutOfRangeException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
-
-
- //
- // GetNextInt32Value -
- //
- // Helper function to read an Int32 token. Takes a boolean "canEndWithoutSeparator".
- //
- private Int32 GetNextInt32Value(Boolean canEndWithoutSeparator)
- {
- String token = GetNextStringValue(canEndWithoutSeparator);
- Int32 value;
- if (!Int32.TryParse(token, NumberStyles.AllowLeadingSign /* "[sign]digits" */, CultureInfo.InvariantCulture, out value))
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- return value;
- }
-
-
- //
- // GetNextAdjustmentRuleArrayValue -
- //
- // Helper function to read an AdjustmentRule[] token. Takes a boolean "canEndWithoutSeparator".
- //
- private AdjustmentRule[] GetNextAdjustmentRuleArrayValue(Boolean canEndWithoutSeparator)
- {
- List<AdjustmentRule> rules = new List<AdjustmentRule>(1);
- int count = 0;
-
- // individual AdjustmentRule array elements do not require semicolons
- AdjustmentRule rule = GetNextAdjustmentRuleValue(true);
- while (rule != null)
- {
- rules.Add(rule);
- count++;
-
- rule = GetNextAdjustmentRuleValue(true);
- }
-
- if (!canEndWithoutSeparator)
- {
- // the AdjustmentRule array must end with a separator
- if (_state == State.EndOfLine)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- }
-
- return (count != 0 ? rules.ToArray() : null);
- }
-
- //
- // GetNextAdjustmentRuleValue -
- //
- // Helper function to read an AdjustmentRule token. Takes a boolean "canEndWithoutSeparator".
- //
- private AdjustmentRule GetNextAdjustmentRuleValue(Boolean canEndWithoutSeparator)
- {
- // first verify the internal state of the object
- if (_state == State.EndOfLine)
- {
- if (canEndWithoutSeparator)
- {
- return null;
- }
- else
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- }
-
- if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- // check to see if the very first token we see is the separator
- if (_serializedText[_currentTokenStartIndex] == sep)
- {
- return null;
- }
-
- // verify the current token is a left-hand-side marker ("[")
- if (_serializedText[_currentTokenStartIndex] != lhs)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- _currentTokenStartIndex++;
-
- DateTime dateStart = GetNextDateTimeValue(false, dateTimeFormat);
- DateTime dateEnd = GetNextDateTimeValue(false, dateTimeFormat);
- TimeSpan daylightDelta = GetNextTimeSpanValue(false);
- TransitionTime daylightStart = GetNextTransitionTimeValue(false);
- TransitionTime daylightEnd = GetNextTransitionTimeValue(false);
- TimeSpan baseUtcOffsetDelta = TimeSpan.Zero;
- // verify that the string is now at the right-hand-side marker ("]") ...
-
- if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- // Check if we have baseUtcOffsetDelta in the serialized string and then deserialize it
- if ((_serializedText[_currentTokenStartIndex] >= '0' && _serializedText[_currentTokenStartIndex] <= '9') ||
- _serializedText[_currentTokenStartIndex] == '-' || _serializedText[_currentTokenStartIndex] == '+')
- {
- baseUtcOffsetDelta = GetNextTimeSpanValue(false);
- }
-
- if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- if (_serializedText[_currentTokenStartIndex] != rhs)
- {
- // skip ahead of any "v.Next" data at the end of the AdjustmentRule
- //
- // FUTURE: if the serialization format is extended in the future then this
- // code section will need to be changed to read the new fields rather
- // than just skipping the data at the end of the [AdjustmentRule].
- SkipVersionNextDataFields(1);
- }
- else
- {
- _currentTokenStartIndex++;
- }
-
- // create the AdjustmentRule from the deserialized fields ...
-
- AdjustmentRule rule;
- try
- {
- rule = AdjustmentRule.CreateAdjustmentRule(dateStart, dateEnd, daylightDelta, daylightStart, daylightEnd, baseUtcOffsetDelta);
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
-
- // finally set the state to either EndOfLine or StartOfToken for the next caller
- if (_currentTokenStartIndex >= _serializedText.Length)
- {
- _state = State.EndOfLine;
- }
- else
- {
- _state = State.StartOfToken;
- }
- return rule;
- }
-
-
- //
- // GetNextTransitionTimeValue -
- //
- // Helper function to read a TransitionTime token. Takes a boolean "canEndWithoutSeparator".
- //
- private TransitionTime GetNextTransitionTimeValue(Boolean canEndWithoutSeparator)
- {
- // first verify the internal state of the object
-
- if (_state == State.EndOfLine
- || (_currentTokenStartIndex < _serializedText.Length
- && _serializedText[_currentTokenStartIndex] == rhs))
- {
- //
- // we are at the end of the line or we are starting at a "]" character
- //
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- // verify the current token is a left-hand-side marker ("[")
-
- if (_serializedText[_currentTokenStartIndex] != lhs)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
- _currentTokenStartIndex++;
-
- Int32 isFixedDate = GetNextInt32Value(false);
-
- if (isFixedDate != 0 && isFixedDate != 1)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- TransitionTime transition;
-
- DateTime timeOfDay = GetNextDateTimeValue(false, timeOfDayFormat);
- timeOfDay = new DateTime(1, 1, 1, timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond);
-
- Int32 month = GetNextInt32Value(false);
-
- if (isFixedDate == 1)
- {
- Int32 day = GetNextInt32Value(false);
-
- try
- {
- transition = TransitionTime.CreateFixedDateRule(timeOfDay, month, day);
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
- else
- {
- Int32 week = GetNextInt32Value(false);
- Int32 dayOfWeek = GetNextInt32Value(false);
-
- try
- {
- transition = TransitionTime.CreateFloatingDateRule(timeOfDay, month, week, (DayOfWeek)dayOfWeek);
- }
- catch (ArgumentException e)
- {
- throw new SerializationException(SR.Serialization_InvalidData, e);
- }
- }
-
- // verify that the string is now at the right-hand-side marker ("]") ...
-
- if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length)
- {
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
- if (_serializedText[_currentTokenStartIndex] != rhs)
- {
- // skip ahead of any "v.Next" data at the end of the AdjustmentRule
- //
- // FUTURE: if the serialization format is extended in the future then this
- // code section will need to be changed to read the new fields rather
- // than just skipping the data at the end of the [TransitionTime].
- SkipVersionNextDataFields(1);
- }
- else
- {
- _currentTokenStartIndex++;
- }
-
- // check to see if the string is now at the separator (";") ...
- Boolean sepFound = false;
- if (_currentTokenStartIndex < _serializedText.Length
- && _serializedText[_currentTokenStartIndex] == sep)
- {
- // handle the case where we ended on a ";"
- _currentTokenStartIndex++;
- sepFound = true;
- }
-
- if (!sepFound && !canEndWithoutSeparator)
- {
- // we MUST end on a separator
- throw new SerializationException(SR.Serialization_InvalidData);
- }
-
-
- // finally set the state to either EndOfLine or StartOfToken for the next caller
- if (_currentTokenStartIndex >= _serializedText.Length)
- {
- _state = State.EndOfLine;
- }
- else
- {
- _state = State.StartOfToken;
- }
- return transition;
- }
- }
- }
-}
diff --git a/src/System.Private.CoreLib/src/System/Type.CoreRT.cs b/src/System.Private.CoreLib/src/System/Type.CoreRT.cs
index 8c1ce1690..30970c349 100644
--- a/src/System.Private.CoreLib/src/System/Type.CoreRT.cs
+++ b/src/System.Private.CoreLib/src/System/Type.CoreRT.cs
@@ -51,6 +51,7 @@ namespace System
return Type.GetTypeFromCLSID(clsid, server, throwOnError);
}
+ [Intrinsic]
public static bool operator ==(Type left, Type right)
{
if (object.ReferenceEquals(left, right))
@@ -62,6 +63,7 @@ namespace System
return left.Equals(right);
}
+ [Intrinsic]
public static bool operator !=(Type left, Type right) => !(left == right);
public bool IsRuntimeImplemented() => this is IRuntimeImplementedType; // Not an api but needs to be public because of Reflection.Core/CoreLib divide.
diff --git a/src/System.Private.CoreLib/src/System/ValueType.cs b/src/System.Private.CoreLib/src/System/ValueType.cs
index d75dd2692..2c71bbcfe 100644
--- a/src/System.Private.CoreLib/src/System/ValueType.cs
+++ b/src/System.Private.CoreLib/src/System/ValueType.cs
@@ -11,8 +11,13 @@
**
===========================================================*/
+using System.Runtime;
+
+using Internal.Runtime.CompilerServices;
using Internal.Runtime.Augments;
+using Debug = System.Diagnostics.Debug;
+
namespace System
{
// CONTRACT with Runtime
@@ -26,6 +31,7 @@ namespace System
return this.GetType().ToString();
}
+#if PROJECTN
public override bool Equals(object obj)
{
return RuntimeAugments.Callbacks.ValueTypeEqualsUsingReflection(this, obj);
@@ -35,5 +41,162 @@ namespace System
{
return RuntimeAugments.Callbacks.ValueTypeGetHashCodeUsingReflection(this);
}
+#else
+ private const int UseFastHelper = -1;
+ private const int GetNumFields = -1;
+
+ // An override of this method will be injected by the compiler into all valuetypes that cannot be compared
+ // using a simple memory comparison.
+ // This API is a bit awkward because we want to avoid burning more than one vtable slot on this.
+ // When index == GetNumFields, this method is expected to return the number of fields of this
+ // valuetype. Otherwise, it returns the offset and type handle of the index-th field on this type.
+ internal virtual int __GetFieldHelper(int index, out EETypePtr eeType)
+ {
+ // Value types that don't override this method will use the fast path that looks at bytes, not fields.
+ Debug.Assert(index == GetNumFields);
+ eeType = default;
+ return UseFastHelper;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null || obj.EETypePtr != this.EETypePtr)
+ return false;
+
+ int numFields = __GetFieldHelper(GetNumFields, out _);
+
+ ref byte thisRawData = ref this.GetRawData();
+ ref byte thatRawData = ref obj.GetRawData();
+
+ if (numFields == UseFastHelper)
+ {
+ // Sanity check - if there are GC references, we should not be comparing bytes
+ Debug.Assert(!this.EETypePtr.HasPointers);
+
+ // Compare the memory
+ int valueTypeSize = (int)this.EETypePtr.ValueTypeSize;
+ for (int i = 0; i < valueTypeSize; i++)
+ {
+ if (Unsafe.Add(ref thisRawData, i) != Unsafe.Add(ref thatRawData, i))
+ return false;
+ }
+ }
+ else
+ {
+ // Foreach field, box and call the Equals method.
+ for (int i = 0; i < numFields; i++)
+ {
+ int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);
+
+ // Fetch the value of the field on both types
+ object thisField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thisRawData, fieldOffset), fieldType);
+ object thatField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thatRawData, fieldOffset), fieldType);
+
+ // Compare the fields
+ if (thisField == null)
+ {
+ if (thatField != null)
+ return false;
+ }
+ else if (!thisField.Equals(thatField))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = this.EETypePtr.GetHashCode();
+
+ hashCode ^= GetHashCodeImpl();
+
+ return hashCode;
+ }
+
+ private int GetHashCodeImpl()
+ {
+ int numFields = __GetFieldHelper(GetNumFields, out _);
+
+ if (numFields == UseFastHelper)
+ return FastGetValueTypeHashCodeHelper(this.EETypePtr, ref this.GetRawData());
+
+ return RegularGetValueTypeHashCode(this.EETypePtr, ref this.GetRawData(), numFields);
+ }
+
+ private static int FastGetValueTypeHashCodeHelper(EETypePtr type, ref byte data)
+ {
+ // Sanity check - if there are GC references, we should not be hashing bytes
+ Debug.Assert(!type.HasPointers);
+
+ int size = (int)type.ValueTypeSize;
+ int hashCode = 0;
+
+ for (int i = 0; i < size / 4; i++)
+ {
+ hashCode ^= Unsafe.As<byte, int>(ref Unsafe.Add(ref data, i * 4));
+ }
+
+ return hashCode;
+ }
+
+ private int RegularGetValueTypeHashCode(EETypePtr type, ref byte data, int numFields)
+ {
+ int hashCode = 0;
+
+ // We only take the hashcode for the first non-null field. That's what the CLR does.
+ for (int i = 0; i < numFields; i++)
+ {
+ int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);
+ ref byte fieldData = ref Unsafe.Add(ref data, fieldOffset);
+
+ Debug.Assert(!fieldType.IsPointer);
+
+ if (fieldType.CorElementType == RuntimeImports.RhCorElementType.ELEMENT_TYPE_R4)
+ {
+ hashCode = Unsafe.Read<float>(ref fieldData).GetHashCode();
+ }
+ else if (fieldType.CorElementType == RuntimeImports.RhCorElementType.ELEMENT_TYPE_R8)
+ {
+ hashCode = Unsafe.Read<double>(ref fieldData).GetHashCode();
+ }
+ else if (fieldType.IsPrimitive)
+ {
+ hashCode = FastGetValueTypeHashCodeHelper(fieldType, ref fieldData);
+ }
+ else if (fieldType.IsValueType)
+ {
+ // We have no option but to box since this value type could have
+ // GC pointers (we could find out if we want though), or fields of type Double/Single (we can't
+ // really find out). Double/Single have weird requirements around -0.0 and +0.0.
+ // If this boxing becomes a problem, we could build a piece of infrastructure that determines the slot
+ // of __GetFieldHelper, decodes the unboxing stub pointed to by the slot to the real target
+ // (we already have that part), and calls the entrypoint that expects a byref `this`, and use the
+ // data to decide between calling fast or regular hashcode helper.
+ var fieldValue = (ValueType)RuntimeImports.RhBox(fieldType, ref fieldData);
+ hashCode = fieldValue.GetHashCodeImpl();
+ }
+ else
+ {
+ object fieldValue = Unsafe.Read<object>(ref fieldData);
+ if (fieldValue != null)
+ {
+ hashCode = fieldValue.GetHashCode();
+ }
+ else
+ {
+ // null object reference, try next
+ continue;
+ }
+ }
+ break;
+ }
+
+ return hashCode;
+ }
+#endif
}
}
diff --git a/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs b/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs
index 4de8b4603..afd236171 100644
--- a/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs
+++ b/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs
@@ -13,23 +13,25 @@ namespace Internal.Runtime.CompilerHelpers
{
internal partial class RuntimeInteropData
{
- public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data)
+ public override IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle)
{
IntPtr openStub, closedStub, delegateCreationStub;
- if (!TryGetMarshallersForDelegate(delegateTypeHandle, out openStub, out closedStub, out delegateCreationStub))
- {
- data = default(McgPInvokeDelegateData);
- return false;
- }
+ GetMarshallersForDelegate(delegateTypeHandle, out openStub, out closedStub, out delegateCreationStub);
+ if (delegateCreationStub == IntPtr.Zero)
+ throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle));
+ return delegateCreationStub;
+ }
- data = new global::System.Runtime.InteropServices.McgPInvokeDelegateData()
- {
- ReverseOpenStaticDelegateStub = openStub,
- ReverseStub = closedStub,
- ForwardDelegateCreationStub = delegateCreationStub
- };
- return true;
+ public override IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate)
+ {
+ IntPtr openStub, closedStub, delegateCreationStub;
+ GetMarshallersForDelegate(delegateTypeHandle, out openStub, out closedStub, out delegateCreationStub);
+ IntPtr pStub = openStaticDelegate ? openStub : closedStub;
+ if (pStub == IntPtr.Zero)
+ throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle));
+ return pStub;
}
+
#region "Struct Data"
public override bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub)
{
@@ -112,7 +114,7 @@ namespace Internal.Runtime.CompilerHelpers
return false;
}
- private unsafe bool TryGetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub)
+ private unsafe bool GetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub)
{
int delegateHashcode = delegateTypeHandle.GetHashCode();
openStub = IntPtr.Zero;
diff --git a/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs b/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs
index 84678d1b9..cfc7ce822 100644
--- a/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs
+++ b/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs
@@ -14,9 +14,22 @@ namespace Internal.Runtime.CompilerHelpers
{
internal partial class RuntimeInteropData : InteropCallbacks
{
- public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data)
+ public override IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle)
{
- return McgModuleManager.GetPInvokeDelegateData(delegateTypeHandle, out data);
+ McgModuleManager.GetPInvokeDelegateData(delegateTypeHandle, out McgPInvokeDelegateData data);
+ IntPtr pStub = data.ForwardDelegateCreationStub;
+ if (pStub == IntPtr.Zero)
+ throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle));
+ return pStub;
+ }
+
+ public override IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate)
+ {
+ McgModuleManager.GetPInvokeDelegateData(delegateTypeHandle, out McgPInvokeDelegateData pinvokeDelegateData);
+ IntPtr pStub = openStaticDelegate ? pinvokeDelegateData.ReverseOpenStaticDelegateStub : pinvokeDelegateData.ReverseStub;
+ if (pStub == IntPtr.Zero)
+ throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle));
+ return pStub;
}
#region "Struct Data"
diff --git a/src/System.Private.Interop/src/Interop/Interop.WinRT.cs b/src/System.Private.Interop/src/Interop/Interop.WinRT.cs
index 3545b0a19..1f27aeb55 100644
--- a/src/System.Private.Interop/src/Interop/Interop.WinRT.cs
+++ b/src/System.Private.Interop/src/Interop/Interop.WinRT.cs
@@ -44,7 +44,7 @@ namespace System.Runtime.InteropServices
[DllImport(Libraries.CORE_WINRT)]
[McgGeneratedNativeCallCodeAttribute]
[MethodImplAttribute(MethodImplOptions.NoInlining)]
- public static extern unsafe int RoActivateInstance(void* hActivableClassId, out void* ppv);
+ public static extern unsafe int RoActivateInstance(void* hActivatableClassId, out void* ppv);
[DllImport(Libraries.CORE_WINRT_ERROR, PreserveSig = true)]
diff --git a/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Unix.cs b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Unix.cs
new file mode 100644
index 000000000..dfd32c719
--- /dev/null
+++ b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Unix.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using System.Security;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// This PInvokeMarshal class should provide full public Marshal
+ /// implementation for all things related to P/Invoke marshalling
+ /// </summary>
+ public partial class PInvokeMarshal
+ {
+ public static void ClearLastWin32Error()
+ {
+ // no-op
+ }
+
+ private static bool IsWin32Atom(IntPtr ptr)
+ {
+ return false;
+ }
+
+ // In CoreRT on Unix, there is not yet a BSTR implementation. On Windows, we would use SysAllocStringLen from OleAut32.dll.
+ internal static IntPtr AllocBSTR(int length)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ internal static void FreeBSTR(IntPtr ptr)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ #region String marshalling
+
+ public static unsafe int ConvertMultiByteToWideChar(byte* multiByteStr,
+ int multiByteLen,
+ char* wideCharStr,
+ int wideCharLen)
+ {
+ return System.Text.Encoding.UTF8.GetChars(multiByteStr, multiByteLen, wideCharStr, wideCharLen);
+ }
+
+ public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr,
+ int wideCharLen,
+ byte* multiByteStr,
+ int multiByteLen,
+ bool bestFit,
+ bool throwOnUnmappableChar)
+ {
+ return System.Text.Encoding.UTF8.GetBytes(wideCharStr, wideCharLen, multiByteStr, multiByteLen);
+ }
+
+ public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr,
+ int wideCharLen,
+ byte* multiByteStr,
+ int multiByteLen)
+ {
+ return System.Text.Encoding.UTF8.GetBytes(wideCharStr, wideCharLen, multiByteStr, multiByteLen);
+ }
+
+ public static unsafe int GetByteCount(char* wideCharStr, int wideCharLen)
+ {
+ return System.Text.Encoding.UTF8.GetByteCount(wideCharStr, wideCharLen);
+ }
+
+ public static unsafe int GetCharCount(byte* multiByteStr, int multiByteLen)
+ {
+ return System.Text.Encoding.UTF8.GetCharCount(multiByteStr, multiByteLen);
+ }
+
+ public static unsafe int GetSystemMaxDBCSCharSize()
+ {
+ return 3;
+ }
+ #endregion
+ }
+}
diff --git a/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Windows.cs b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Windows.cs
new file mode 100644
index 000000000..6b8554756
--- /dev/null
+++ b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.Windows.cs
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// This PInvokeMarshal class should provide full public Marshal
+ /// implementation for all things related to P/Invoke marshalling
+ /// </summary>
+ public partial class PInvokeMarshal
+ {
+ private const long HIWORDMASK = unchecked((long)0xffffffffffff0000L);
+
+ // Win32 has the concept of Atoms, where a pointer can either be a pointer
+ // or an int. If it's less than 64K, this is guaranteed to NOT be a
+ // pointer since the bottom 64K bytes are reserved in a process' page table.
+ // We should be careful about deallocating this stuff. Extracted to
+ // a function to avoid C# problems with lack of support for IntPtr.
+ // We have 2 of these methods for slightly different semantics for NULL.
+ private static bool IsWin32Atom(IntPtr ptr)
+ {
+ long lPtr = (long)ptr;
+ return 0 == (lPtr & HIWORDMASK);
+ }
+
+ private static bool IsNotWin32Atom(IntPtr ptr)
+ {
+ long lPtr = (long)ptr;
+ return 0 != (lPtr & HIWORDMASK);
+ }
+ public static void ClearLastWin32Error()
+ {
+ Interop.mincore.SetLastError(0);
+ }
+
+ #region String marshalling
+
+ public static unsafe int ConvertMultiByteToWideChar(byte* buffer, int ansiLength, char* pWChar, int uniLength)
+ {
+ return Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, 0, buffer, ansiLength, pWChar, uniLength);
+ }
+
+ // Convert a UTF16 string to ANSI byte array
+ public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, int wideCharLen, byte* multiByteStr, int multiByteLen)
+ {
+ return Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP,
+ 0,
+ wideCharStr,
+ wideCharLen,
+ multiByteStr,
+ multiByteLen,
+ default(IntPtr),
+ default(IntPtr)
+ );
+ }
+
+ // Convert a UTF16 string to ANSI byte array using flags
+ public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr,
+ int wideCharLen,
+ byte* multiByteStr,
+ int multiByteLen,
+ bool bestFit,
+ bool throwOnUnmappableChar)
+ {
+ uint flags = (bestFit ? 0 : Interop.Kernel32.WC_NO_BEST_FIT_CHARS);
+ int defaultCharUsed = 0;
+ int ret = Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP,
+ flags,
+ wideCharStr,
+ wideCharLen,
+ multiByteStr,
+ multiByteLen,
+ default(IntPtr),
+ throwOnUnmappableChar ? new System.IntPtr(&defaultCharUsed) : default(IntPtr)
+ );
+ if (defaultCharUsed != 0)
+ {
+ throw new ArgumentException(SR.Arg_InteropMarshalUnmappableChar);
+ }
+
+ return ret;
+ }
+
+ // Return size in bytes required to convert a UTF16 string to byte array.
+ public static unsafe int GetByteCount(char* wStr, int wideStrLen)
+ {
+ return Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP,
+ 0,
+ wStr,
+ wideStrLen,
+ default(byte*),
+ 0,
+ default(IntPtr),
+ default(IntPtr)
+ );
+ }
+
+ // Return number of charaters encoded in native byte array lpMultiByteStr
+ unsafe public static int GetCharCount(byte* multiByteStr, int multiByteLen)
+ {
+ return Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, 0, multiByteStr, multiByteLen, default(char*), 0);
+ }
+
+ public static unsafe int GetSystemMaxDBCSCharSize()
+ {
+ Interop.Kernel32.CPINFO cpInfo;
+ if (Interop.Kernel32.GetCPInfo(Interop.Kernel32.CP_ACP, &cpInfo) != 0)
+ {
+ return cpInfo.MaxCharSize;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ #endregion
+ }
+}
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ internal const string Kernel32 = "kernel32.dll";
+ }
+
+ internal partial class Kernel32
+ {
+ [DllImport(Libraries.Kernel32)]
+ internal static extern unsafe int WideCharToMultiByte(
+ uint CodePage, uint dwFlags,
+ char* lpWideCharStr, int cchWideChar,
+ byte* lpMultiByteStr, int cbMultiByte,
+ IntPtr lpDefaultChar, IntPtr lpUsedDefaultChar);
+
+ internal const uint CP_ACP = 0;
+ internal const uint WC_NO_BEST_FIT_CHARS = 0x00000400;
+
+ internal unsafe struct CPINFO
+ {
+ internal int MaxCharSize;
+
+ internal fixed byte DefaultChar[2 /* MAX_DEFAULTCHAR */];
+ internal fixed byte LeadByte[12 /* MAX_LEADBYTES */];
+ }
+
+ [DllImport(Libraries.Kernel32)]
+ internal static extern unsafe int GetCPInfo(uint codePage, CPINFO* lpCpInfo);
+ }
+}
diff --git a/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs
index 46da09e11..4287d0e16 100644
--- a/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs
+++ b/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Debug = System.Diagnostics.Debug;
using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices
@@ -11,34 +12,32 @@ namespace System.Runtime.InteropServices
/// implementation for all things related to P/Invoke marshalling
/// </summary>
[CLSCompliant(false)]
- public sealed class PInvokeMarshal
- {
- public static void SaveLastWin32Error()
- {
- // nop
- }
-
- public static void ClearLastWin32Error()
- {
- // nop
- }
+ public partial class PInvokeMarshal
+ {
+ [ThreadStatic]
+ internal static int s_lastWin32Error;
public static int GetLastWin32Error()
{
- return 0;
+ return s_lastWin32Error;
}
public static void SetLastWin32Error(int errorCode)
{
- // nop
+ s_lastWin32Error = errorCode;
+ }
+
+ public static void SaveLastWin32Error()
+ {
+ s_lastWin32Error = Marshal.GetLastWin32Error();
}
- public static IntPtr GetStubForPInvokeDelegate(Delegate del)
+ public static IntPtr GetFunctionPointerForDelegate(Delegate del)
{
return IntPtr.Zero;
}
- public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType)
+ public static Delegate GetDelegateForFunctionPointer(IntPtr pStub, RuntimeTypeHandle delegateType)
{
return default(Delegate);
}
@@ -85,124 +84,410 @@ namespace System.Runtime.InteropServices
public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination)
{
- // nop
+ stringBuilder.UnsafeCopyTo((char*)destination);
}
public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder)
{
- // nop
+ stringBuilder.ReplaceBuffer((char*)newBuffer);
}
public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative,
bool bestFit, bool throwOnUnmappableChar)
{
- // nop
+ int len;
+
+ // Convert StringBuilder to UNICODE string
+ // Optimize for the most common case. If there is only a single char[] in the StringBuilder,
+ // get it and convert it to ANSI
+ char[] buffer = stringBuilder.GetBuffer(out len);
+ if (buffer != null)
+ {
+ fixed (char* pManaged = buffer)
+ {
+ StringToAnsiString(pManaged, len, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar);
+ }
+ }
+ else // Otherwise, convert StringBuilder to string and then convert to ANSI
+ {
+ string str = stringBuilder.ToString();
+
+ // Convert UNICODE string to ANSI string
+ fixed (char* pManaged = str)
+ {
+ StringToAnsiString(pManaged, str.Length, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar);
+ }
+ }
}
public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder)
{
- // nop
- }
-
+ if (newBuffer == null)
+ throw new ArgumentNullException(nameof(newBuffer));
+
+ int lenAnsi;
+ int lenUnicode;
+ CalculateStringLength(newBuffer, out lenAnsi, out lenUnicode);
+
+ if (lenUnicode > 0)
+ {
+ char[] buffer = new char[lenUnicode];
+ fixed (char* pTemp = &buffer[0])
+ {
+ ConvertMultiByteToWideChar(newBuffer,
+ lenAnsi,
+ pTemp,
+ lenUnicode);
+ }
+ stringBuilder.ReplaceBuffer(buffer);
+ }
+ else
+ {
+ stringBuilder.Clear();
+ }
+ }
+
public static unsafe string AnsiStringToString(byte* pchBuffer)
{
- return default(string);
+ if (pchBuffer == null)
+ {
+ return null;
+ }
+
+ int lenAnsi;
+ int lenUnicode;
+ CalculateStringLength(pchBuffer, out lenAnsi, out lenUnicode);
+
+ string result = String.Empty;
+
+ if (lenUnicode > 0)
+ {
+ result = new string(' ',lenUnicode);
+
+ fixed (char* pTemp = result)
+ {
+ ConvertMultiByteToWideChar(pchBuffer,
+ lenAnsi,
+ pTemp,
+ lenUnicode);
+ }
+ }
+
+ return result;
}
public static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar)
{
- return default(byte*);
+ if (str != null)
+ {
+ int lenUnicode = str.Length;
+
+ fixed (char* pManaged = str)
+ {
+ return StringToAnsiString(pManaged, lenUnicode, null, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar);
+ }
+ }
+
+ return null;
}
public static unsafe void ByValWideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, int expectedCharCount,
bool bestFit, bool throwOnUnmappableChar)
{
- // nop
+ // Zero-init pNative if it is NULL
+ if (managedArray == null)
+ {
+ // @TODO - Create a more efficient version of zero initialization
+ for (int i = 0; i < expectedCharCount; i++)
+ {
+ pNative[i] = 0;
+ }
+ }
+
+
+ int lenUnicode = managedArray.Length;
+ if (lenUnicode < expectedCharCount)
+
+ throw new ArgumentException(SR.WrongSizeArrayInNStruct);
+
+ fixed (char* pManaged = managedArray)
+ {
+ StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/false, bestFit, throwOnUnmappableChar);
+ }
}
public static unsafe void ByValAnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray)
{
- // nop
+ // This should never happen because it is a embedded array
+ Debug.Assert(pNative != null);
+
+ // This should never happen because the array is always allocated by the marshaller
+ Debug.Assert(managedArray != null);
+
+ // COMPAT: Use the managed array length as the maximum length of native buffer
+ // This obviously doesn't make sense but desktop CLR does that
+ int lenInBytes = managedArray.Length;
+ fixed (char* pManaged = managedArray)
+ {
+ ConvertMultiByteToWideChar(pNative,
+ lenInBytes,
+ pManaged,
+ lenInBytes);
+ }
}
public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar)
{
- // nop
+ // Do nothing if array is NULL. This matches desktop CLR behavior
+ if (managedArray == null)
+ return;
+
+ // Desktop CLR crash (AV at runtime) - we can do better in .NET Native
+ if (pNative == null)
+ throw new ArgumentNullException(nameof(pNative));
+
+ int lenUnicode = managedArray.Length;
+ fixed (char* pManaged = managedArray)
+ {
+ StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/false, bestFit, throwOnUnmappableChar);
+ }
}
public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray)
{
- // nop
+ // Do nothing if native is NULL. This matches desktop CLR behavior
+ if (pNative == null)
+ return;
+
+ // Desktop CLR crash (AV at runtime) - we can do better in .NET Native
+ if (managedArray == null)
+ throw new ArgumentNullException(nameof(managedArray));
+
+ // COMPAT: Use the managed array length as the maximum length of native buffer
+ // This obviously doesn't make sense but desktop CLR does that
+ int lenInBytes = managedArray.Length;
+ fixed (char* pManaged = managedArray)
+ {
+ ConvertMultiByteToWideChar(pNative,
+ lenInBytes,
+ pManaged,
+ lenInBytes);
+ }
}
public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar)
{
- return default(byte);
+ // @TODO - we really shouldn't allocate one-byte arrays and then destroy it
+ byte* nativeArray = StringToAnsiString(&managedValue, 1, null, /*terminateWithNull=*/false, bestFit, throwOnUnmappableChar);
+ byte native = (*nativeArray);
+ CoTaskMemFree(new IntPtr(nativeArray));
+ return native;
}
public static unsafe char AnsiCharToWideChar(byte nativeValue)
{
- return default(char);
+ char ch;
+ ConvertMultiByteToWideChar(&nativeValue, 1, &ch, 1);
+ return ch;
}
public static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar, bool truncate = true)
{
- // nop
+ if (pNative == null)
+ throw new ArgumentNullException(nameof(pNative));
+
+ if (str != null)
+ {
+ // Truncate the string if it is larger than specified by SizeConst
+ int lenUnicode;
+
+ if (truncate)
+ {
+ lenUnicode = str.Length;
+ if (lenUnicode >= charCount)
+ lenUnicode = charCount - 1;
+ }
+ else
+ {
+ lenUnicode = charCount;
+ }
+
+ fixed (char* pManaged = str)
+ {
+ StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar);
+ }
+ }
+ else
+ {
+ (*pNative) = (byte)'\0';
+ }
}
public static unsafe string ByValAnsiStringToString(byte* pchBuffer, int charCount)
{
- return default(string);
- }
+ // Match desktop CLR behavior
+ if (charCount == 0)
+ throw new MarshalDirectiveException();
- public static unsafe int ConvertMultiByteToWideChar(byte* buffer, int ansiLength, char* pWChar, int uniLength)
- {
- return default(int);
- }
+ int lenAnsi = GetAnsiStringLen(pchBuffer);
+ int lenUnicode = charCount;
- public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, int wideCharLen, byte* multiByteStr, int multiByteLen)
- {
- return default(int);
- }
+ string result = String.Empty;
- public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr,
- int wideCharLen,
- byte* multiByteStr,
- int multiByteLen,
- uint flags,
- IntPtr usedDefaultChar)
- {
- return default(int);
- }
+ if (lenUnicode > 0)
+ {
+ char* unicodeBuf = stackalloc char[lenUnicode];
+ int unicodeCharWritten = ConvertMultiByteToWideChar(pchBuffer,
+ lenAnsi,
+ unicodeBuf,
+ lenUnicode);
- public static unsafe int GetByteCount(char* wStr, int wideStrLen)
- {
- return default(int);
- }
+ // If conversion failure, return empty string to match desktop CLR behavior
+ if (unicodeCharWritten > 0)
+ result = new string(unicodeBuf, 0, unicodeCharWritten);
+ }
- unsafe public static int GetCharCount(byte* multiByteStr, int multiByteLen)
+ return result;
+ }
+
+
+ private static unsafe int GetAnsiStringLen(byte* pchBuffer)
{
- return default(int);
+ byte* pchBufferOriginal = pchBuffer;
+ while (*pchBuffer != 0)
+ {
+ pchBuffer++;
+ }
+
+ return (int)(pchBuffer - pchBufferOriginal);
}
- public static unsafe int GetSystemMaxDBCSCharSize()
+ // c# string (UTF-16) to UTF-8 encoded byte array
+ private static unsafe byte* StringToAnsiString(char* pManaged, int lenUnicode, byte* pNative, bool terminateWithNull,
+ bool bestFit, bool throwOnUnmappableChar)
{
- return default(int);
+ bool allAscii = true;
+
+ for (int i = 0; i < lenUnicode; i++)
+ {
+ if (pManaged[i] >= 128)
+ {
+ allAscii = false;
+ break;
+ }
+ }
+
+ int length;
+
+ if (allAscii) // If all ASCII, map one UNICODE character to one ANSI char
+ {
+ length = lenUnicode;
+ }
+ else // otherwise, let OS count number of ANSI chars
+ {
+ length = GetByteCount(pManaged, lenUnicode);
+ }
+
+ if (pNative == null)
+ {
+ pNative = (byte*)CoTaskMemAlloc((System.UIntPtr)(length + 1));
+ }
+ if (allAscii) // ASCII conversion
+ {
+ byte* pDst = pNative;
+ char* pSrc = pManaged;
+
+ while (lenUnicode > 0)
+ {
+ unchecked
+ {
+ *pDst++ = (byte)(*pSrc++);
+ lenUnicode--;
+ }
+ }
+ }
+ else // Let OS convert
+ {
+ ConvertWideCharToMultiByte(pManaged,
+ lenUnicode,
+ pNative,
+ length,
+ bestFit,
+ throwOnUnmappableChar);
+ }
+
+ // Zero terminate
+ if (terminateWithNull)
+ *(pNative + length) = 0;
+
+ return pNative;
}
public static unsafe String PtrToStringUni(IntPtr ptr, int len)
{
- return default(String);
+ return Marshal.PtrToStringUni(ptr, len);
}
public static unsafe String PtrToStringUni(IntPtr ptr)
{
- return default(String);
+ return Marshal.PtrToStringUni(ptr);
}
-
+
+ public static unsafe String PtrToStringAnsi(IntPtr ptr)
+ {
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+
+ public static unsafe String PtrToStringAnsi(IntPtr ptr, int len)
+ {
+ return Marshal.PtrToStringAnsi(ptr, len);
+ }
+
+ //====================================================================
+ // Copy blocks from CLR arrays to native memory.
+ //====================================================================
public static unsafe void CopyToNative(Array source, int startIndex, IntPtr destination, int length)
{
- // nop
- }
+ throw new PlatformNotSupportedException();
+ }
+
+ /// <summary>
+ /// This is a auxiliary function that counts the length of the ansi buffer and
+ /// estimate the length of the buffer in Unicode. It returns true if all bytes
+ /// in the buffer are ANSII.
+ /// </summary>
+ private static unsafe bool CalculateStringLength(byte* pchBuffer, out int ansiBufferLen, out int unicodeBufferLen)
+ {
+ ansiBufferLen = 0;
+
+ bool allAscii = true;
+
+ {
+ byte* p = pchBuffer;
+ byte b = *p++;
+
+ while (b != 0)
+ {
+ if (b >= 128)
+ {
+ allAscii = false;
+ }
+
+ ansiBufferLen++;
+
+ b = *p++;
+ }
+ }
+
+ if (allAscii)
+ {
+ unicodeBufferLen = ansiBufferLen;
+ }
+ else // If non ASCII, let OS calculate number of characters
+ {
+ unicodeBufferLen = GetCharCount(pchBuffer, ansiBufferLen);
+ }
+ return allAscii;
+ }
}
}
diff --git a/src/System.Private.Interop/src/Resources/Strings.resx b/src/System.Private.Interop/src/Resources/Strings.resx
index a2d51ec82..636774c6b 100644
--- a/src/System.Private.Interop/src/Resources/Strings.resx
+++ b/src/System.Private.Interop/src/Resources/Strings.resx
@@ -366,4 +366,10 @@
<data name="TypeNotDelegate" xml:space="preserve">
<value>'Type '{0}' is not a delegate type. EventTokenTable may only be used with delegate types.'</value>
</data>
+ <data name="WrongSizeArrayInNStruct" xml:space="preserve">
+ <value>Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout.</value>
+ </data>
+ <data name="Arg_InteropMarshalUnmappableChar" xml:space="preserve">
+ <value>Cannot marshal: Encountered unmappable character.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/System.Private.Interop/src/Shared/ComCallableObject.cs b/src/System.Private.Interop/src/Shared/ComCallableObject.cs
index 04ad4e487..7bce62ca8 100644
--- a/src/System.Private.Interop/src/Shared/ComCallableObject.cs
+++ b/src/System.Private.Interop/src/Shared/ComCallableObject.cs
@@ -250,7 +250,7 @@ namespace System.Runtime.InteropServices
/// Combined Ref Count of COM and Jupiter
/// COM takes lower 32-bit while Jupiter takes higher 32-bit.
/// It's OK for COM ref count to overflow
- /// Be verycareful when you read this because you can't read this atomicly under x86 directly.
+ /// Be verycareful when you read this because you can't read this atomically under x86 directly.
/// Only use CombinedRefCount property for thread-safety
/// </summary>
long m_lRefCount;
diff --git a/src/System.Private.Interop/src/Shared/Marshal.cs b/src/System.Private.Interop/src/Shared/Marshal.cs
index 4dfbd0bff..6a0be9a50 100644
--- a/src/System.Private.Interop/src/Shared/Marshal.cs
+++ b/src/System.Private.Interop/src/Shared/Marshal.cs
@@ -93,7 +93,8 @@ namespace System.Runtime.InteropServices
createCOMException : true,
hasErrorInfo: false);
#else
- return new Exception(errorCode.ToString());
+ // TODO: Map HR to exeption even without COM interop support?
+ return new COMException(errorCode);
#endif
}
}
diff --git a/src/System.Private.Interop/src/Shared/McgMarshal.cs b/src/System.Private.Interop/src/Shared/McgMarshal.cs
index bf51b8d35..d6ef590c2 100644
--- a/src/System.Private.Interop/src/Shared/McgMarshal.cs
+++ b/src/System.Private.Interop/src/Shared/McgMarshal.cs
@@ -990,7 +990,7 @@ namespace System.Runtime.InteropServices
public static int GetHRForExceptionWinRT(Exception ex)
{
#if ENABLE_WINRT
- return ExceptionHelpers.GetHRForExceptionWithErrorPropogationNoThrow(ex, true);
+ return ExceptionHelpers.GetHRForExceptionWithErrorPropagationNoThrow(ex, true);
#else
// TODO : ExceptionHelpers should be platform specific , move it to
// seperate source files
@@ -1003,7 +1003,7 @@ namespace System.Runtime.InteropServices
public static int GetHRForException(Exception ex)
{
#if ENABLE_WINRT
- return ExceptionHelpers.GetHRForExceptionWithErrorPropogationNoThrow(ex, false);
+ return ExceptionHelpers.GetHRForExceptionWithErrorPropagationNoThrow(ex, false);
#else
return ex.HResult;
#endif
@@ -1033,7 +1033,8 @@ namespace System.Runtime.InteropServices
#elif CORECLR
return Marshal.GetExceptionForHR(hr);
#else
- return new COMException(hr.ToString(), hr);
+ // TODO: Map HR to exeption even without COM interop support?
+ return new COMException(hr);
#endif
}
@@ -1117,7 +1118,7 @@ namespace System.Runtime.InteropServices
#if CORECLR
throw new NotSupportedException();
#else
- return PInvokeMarshal.GetStubForPInvokeDelegate(dele);
+ return PInvokeMarshal.GetFunctionPointerForDelegate(dele);
#endif
}
@@ -1141,7 +1142,7 @@ namespace System.Runtime.InteropServices
pStub
);
#else
- return PInvokeMarshal.GetPInvokeDelegateForStub(pStub, delegateType);
+ return PInvokeMarshal.GetDelegateForFunctionPointer(pStub, delegateType);
#endif
}
diff --git a/src/System.Private.Interop/src/Shared/McgModuleManager.cs b/src/System.Private.Interop/src/Shared/McgModuleManager.cs
index 0c7856dcb..365509a58 100644
--- a/src/System.Private.Interop/src/Shared/McgModuleManager.cs
+++ b/src/System.Private.Interop/src/Shared/McgModuleManager.cs
@@ -724,11 +724,7 @@ namespace System.Runtime.InteropServices
return true;
}
}
-#if ENABLE_WINRT
- throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateType));
-#else
return false;
-#endif
}
#endregion
diff --git a/src/Common/src/System/Runtime/InteropServices/McgPInvokeData.cs b/src/System.Private.Interop/src/Shared/McgPInvokeData.cs
index ceaaa1e51..c0d01fce6 100644
--- a/src/Common/src/System/Runtime/InteropServices/McgPInvokeData.cs
+++ b/src/System.Private.Interop/src/Shared/McgPInvokeData.cs
@@ -102,25 +102,4 @@ namespace System.Runtime.InteropServices
/// </summary>
public IntPtr ForwardDelegateCreationStub;
}
-
-
- /// <summary>
- /// Base class for all 'wrapper' classes that wraps a native function pointer
- /// The forward delegates (that wraps native function pointers) points to derived Invoke method of this
- /// class, and the Invoke method would implement the marshalling and making the call
- /// </summary>
- public abstract class NativeFunctionPointerWrapper
- {
- public NativeFunctionPointerWrapper(IntPtr nativeFunctionPointer)
- {
- m_nativeFunctionPointer = nativeFunctionPointer;
- }
-
- IntPtr m_nativeFunctionPointer;
-
- public IntPtr NativeFunctionPointer
- {
- get { return m_nativeFunctionPointer; }
- }
- }
}
diff --git a/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs b/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs
index a6e80880c..25496e665 100644
--- a/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs
+++ b/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs
@@ -1213,7 +1213,7 @@ namespace System.Runtime.InteropServices
#region "CCWTemplate Data"
internal static string GetCCWRuntimeClassName(this RuntimeTypeHandle ccwType)
{
- // Special case for Object type to aligh with desktop behavior
+ // Special case for Object type to align with desktop behavior
if (ccwType.Equals(typeof(Object).TypeHandle))
return default(string);
diff --git a/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj b/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj
index 443e811f7..00f7d24f4 100644
--- a/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj
+++ b/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj
@@ -50,12 +50,10 @@
<Compile Include="InteropExtensions\PreInitializedAttribute.cs" />
<Compile Include="InteropExtensions\PInvokeMarshal.cs" />
<Compile Include="Interop\Interop.PlatformNotSupported.cs" />
- <Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgPInvokeData.cs">
- <Link>System\Runtime\InteropServices\McgPInvokeData.cs</Link>
- </Compile>
<Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs">
<Link>System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs</Link>
</Compile>
+ <Compile Include="System\Runtime\InteropServices\MarshalAdapter.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
@@ -63,7 +61,24 @@
<Compile Include="Interop\Interop.WinRT.Basic.cs" />
<Compile Include="System\Runtime\InteropServices\Variant.cs" />
</ItemGroup>
+
+ <ItemGroup Condition="'$(TargetsWindows)' == 'true'">
+ <Compile Include="InteropExtensions\PInvokeMarshal.Windows.cs" />
+ <Compile Include="..\..\Common\src\Interop\Windows\kernel32\Interop.MultiByteToWideChar.cs">
+ <Link>Interop\Windows\kernel32\Interop.MultiByteToWideChar.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.SetLastError.cs">
+ <Link>Interop\Windows\mincore\Interop.SetLastError.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\Interop.Libraries.cs">
+ <Link>Interop\Windows\Interop.Libraries.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup Condition="'$(TargetsUnix)'=='true'">
+ <Compile Include="InteropExtensions\PInvokeMarshal.Unix.cs" />
+ </ItemGroup>
+
<ItemGroup>
<Compile Include="System\Reflection\DispatchProxy.cs" />
<Compile Include="System\Reflection\DispatchProxyEntry.cs" />
diff --git a/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj b/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj
index efe03b78e..04be68423 100644
--- a/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj
+++ b/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj
@@ -54,12 +54,10 @@
<Compile Include="InteropExtensions\PreInitializedAttribute.cs" />
<Compile Include="InteropExtensions\PInvokeMarshal.cs" />
<Compile Include="Interop\Interop.PlatformNotSupported.cs" />
- <Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgPInvokeData.cs">
- <Link>System\Runtime\InteropServices\McgPInvokeData.cs</Link>
- </Compile>
<Compile Include="..\..\Common\src\System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs">
<Link>System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs</Link>
</Compile>
+ <Compile Include="System\Runtime\InteropServices\MarshalAdapter.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
@@ -101,7 +99,24 @@
<Compile Include="Windows\Foundation\Size.cs" />
<Compile Include="Windows\Foundation\TokenizerHelper.cs" />
</ItemGroup>
-
+
+ <ItemGroup Condition="'$(TargetsWindows)' == 'true'">
+ <Compile Include="InteropExtensions\PInvokeMarshal.Windows.cs" />
+ <Compile Include="..\..\Common\src\Interop\Windows\kernel32\Interop.MultiByteToWideChar.cs">
+ <Link>Interop\Windows\kernel32\Interop.MultiByteToWideChar.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\mincore\Interop.SetLastError.cs">
+ <Link>Interop\Windows\mincore\Interop.SetLastError.cs</Link>
+ </Compile>
+ <Compile Include="..\..\Common\src\Interop\Windows\Interop.Libraries.cs">
+ <Link>Interop\Windows\Interop.Libraries.cs</Link>
+ </Compile>
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(TargetsUnix)'=='true'">
+ <Compile Include="InteropExtensions\PInvokeMarshal.Unix.cs" />
+ </ItemGroup>
+
<Import Project="System.Private.Interop.Shared.projitems" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
diff --git a/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems b/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems
index 394ff286f..a76881f51 100644
--- a/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems
+++ b/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems
@@ -11,8 +11,6 @@
<Compile Include="System\Runtime\InteropServices\ComWeakReferenceHelpers.cs" />
<Compile Include="System\Runtime\InteropServices\InteropEventProvider.cs" />
- <Compile Include="System\Runtime\InteropServices\MarshalAdapter.cs" />
- <Compile Include="System\Runtime\InteropServices\MarshalImpl.cs" />
<Compile Include="Shared\ComCallableObject.cs" />
<Compile Include="Shared\ComInterop.cs" />
@@ -38,6 +36,7 @@
<Compile Include="Shared\McgMarshal.cs" />
<Compile Include="Shared\McgModule.cs" />
<Compile Include="Shared\McgModuleManager.cs" />
+ <Compile Include="Shared\McgPInvokeData.cs" />
<Compile Include="Shared\McgPInvokeMarshalStubAttribute.cs" />
<Compile Include="Shared\McgredirectedMethodAttribute.cs" />
<Compile Include="Shared\McgRedirectedTypeAttribute.cs" />
diff --git a/src/System.Private.Interop/src/System.Private.Interop.csproj b/src/System.Private.Interop/src/System.Private.Interop.csproj
index 03ca92928..73411b210 100644
--- a/src/System.Private.Interop/src/System.Private.Interop.csproj
+++ b/src/System.Private.Interop/src/System.Private.Interop.csproj
@@ -144,6 +144,10 @@
<Compile Condition="'$(IsProjectNLibrary)' == 'true'" Include="Internal\Runtime\CompilerHelpers\RuntimeInteropData.ProjectN.cs"/>
<Compile Condition="'$(IsProjectNLibrary)' != 'true'" Include="Internal\Runtime\CompilerHelpers\RuntimeInteropData.CoreRT.cs"/>
+
+ <!-- TODO This should be really only needed for CoreCLR flavor of S.P.Interop. -->
+ <!-- See https://github.com/dotnet/corert/pull/5404 -->
+ <Compile Condition="'$(IsProjectNLibrary)' == 'true'" Include="System\Runtime\InteropServices\MarshalAdapter.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsProjectNLibrary)' != 'true'">
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/bindptr.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/bindptr.cs
index 3405ef9a9..1dde60ab2 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/bindptr.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/bindptr.cs
@@ -9,10 +9,13 @@ namespace System.Runtime.InteropServices.ComTypes
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct BINDPTR
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
[FieldOffset(0)]
public IntPtr lpfuncdesc;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
[FieldOffset(0)]
public IntPtr lpvardesc;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
[FieldOffset(0)]
public IntPtr lptcomp;
}
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/dispparams.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/dispparams.cs
index bea6e7c37..404c768ff 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/dispparams.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/dispparams.cs
@@ -9,7 +9,9 @@ namespace System.Runtime.InteropServices.ComTypes
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPPARAMS
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr rgvarg;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr rgdispidNamedArgs;
public int cArgs;
public int cNamedArgs;
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/excepinfo.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/excepinfo.cs
index bab6e5fa8..9a0187dda 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/excepinfo.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/excepinfo.cs
@@ -18,7 +18,9 @@ namespace System.Runtime.InteropServices.ComTypes
[MarshalAs(UnmanagedType.BStr)]
public String bstrHelpFile;
public int dwHelpContext;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr pvReserved;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr pfnDeferredFillIn;
public Int32 scode;
}
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/formatetc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/formatetc.cs
index 2365cfe9a..b443c2b34 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/formatetc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/formatetc.cs
@@ -10,6 +10,7 @@ namespace System.Runtime.InteropServices.ComTypes
{
[MarshalAs(UnmanagedType.U2)]
public short cfFormat;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr ptd;
[MarshalAs(UnmanagedType.U4)]
public DVASPECT dwAspect;
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/funcdesc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/funcdesc.cs
index ab249c469..63e665339 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/funcdesc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/funcdesc.cs
@@ -10,7 +10,9 @@ namespace System.Runtime.InteropServices.ComTypes
public struct FUNCDESC
{
public int memid; //MEMBERID memid;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr lprgscode; // /* [size_is(cScodes)] */ SCODE RPC_FAR *lprgscode;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr lprgelemdescParam; // /* [size_is(cParams)] */ ELEMDESC __RPC_FAR *lprgelemdescParam;
public FUNCKIND funckind; //FUNCKIND funckind;
public INVOKEKIND invkind; //INVOKEKIND invkind;
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/idldesc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/idldesc.cs
index 189287924..31493355e 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/idldesc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/idldesc.cs
@@ -9,6 +9,7 @@ namespace System.Runtime.InteropServices.ComTypes
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IDLDESC
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr dwReserved;
public IDLFLAG wIDLFlags;
}
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/paramdesc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/paramdesc.cs
index bc0d107e1..809ce145c 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/paramdesc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/paramdesc.cs
@@ -9,6 +9,7 @@ namespace System.Runtime.InteropServices.ComTypes
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PARAMDESC
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr lpVarValue;
public PARAMFLAG wParamFlags;
}
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/stgmedium.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/stgmedium.cs
index cdb8015ac..77505cd72 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/stgmedium.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/stgmedium.cs
@@ -9,6 +9,7 @@ namespace System.Runtime.InteropServices.ComTypes
public struct STGMEDIUM
{
public TYMED tymed;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr unionmember;
[MarshalAs(UnmanagedType.IUnknown)]
public object pUnkForRelease;
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typeattr.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typeattr.cs
index bf4488b72..3e6381192 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typeattr.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typeattr.cs
@@ -18,6 +18,7 @@ namespace System.Runtime.InteropServices.ComTypes
public Int32 dwReserved;
public Int32 memidConstructor;
public Int32 memidDestructor;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr lpstrSchema;
public Int32 cbSizeInstance;
public TYPEKIND typekind;
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typedesc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typedesc.cs
index 29d9a2e9e..696f439ad 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typedesc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/typedesc.cs
@@ -9,6 +9,7 @@ namespace System.Runtime.InteropServices.ComTypes
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TYPEDESC
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
public IntPtr lpValue;
public Int16 vt;
}
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/vardesc.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/vardesc.cs
index 2a349f42a..abc4b70d7 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/vardesc.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/ComTypes/vardesc.cs
@@ -17,6 +17,7 @@ namespace System.Runtime.InteropServices.ComTypes
{
[FieldOffset(0)]
public int oInst;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible", Justification="Backwards compatibility")]
[FieldOffset(0)]
public IntPtr lpvarValue;
};
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs
index f9e481abc..d565e199f 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs
@@ -2,13 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-//
-// This file provides an implementation of the pieces of the Marshal class which are required by the Interop
-// API contract but are not provided by the version of Marshal which is part of the Redhawk test library.
-// This partial class is combined with the version from the Redhawk test library, in order to provide the
-// Marshal implementation for System.Private.CoreLib.
-//
-
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -575,7 +568,7 @@ namespace System.Runtime.InteropServices
createCOMException: false,
hasErrorInfo: false);
#else
- throw new PlatformNotSupportedException("GetExceptionForHR");
+ return new COMException(errorCode);
#endif // ENABLE_WINRT
}
@@ -763,7 +756,7 @@ namespace System.Runtime.InteropServices
{
throw new ArgumentNullException(nameof(o));
}
- return MarshalAdapter.GetIUnknownForObject(o);
+ return McgMarshal.ObjectToComInterface(o, InternalTypes.IUnknown);
}
//====================================================================
@@ -775,7 +768,7 @@ namespace System.Runtime.InteropServices
{
throw new ArgumentNullException(nameof(pUnk));
}
- return MarshalAdapter.GetObjectForIUnknown(pUnk);
+ return McgMarshal.ComInterfaceToObject(pUnk, InternalTypes.IUnknown);
}
//====================================================================
@@ -1018,7 +1011,7 @@ namespace System.Runtime.InteropServices
if (d == null)
throw new ArgumentNullException(nameof(d));
- return PInvokeMarshal.GetStubForPInvokeDelegate(d);
+ return PInvokeMarshal.GetFunctionPointerForDelegate(d);
}
public static IntPtr GetFunctionPointerForDelegate<TDelegate>(TDelegate d)
@@ -1268,7 +1261,13 @@ namespace System.Runtime.InteropServices
public static IntPtr /* IUnknown* */ GetComInterfaceForObject(Object o, Type T)
{
- return MarshalAdapter.GetComInterfaceForObject(o, T);
+ if (o == null)
+ throw new ArgumentNullException(nameof(o));
+
+ if (T == null)
+ throw new ArgumentNullException(nameof(T));
+
+ return McgMarshal.ObjectToComInterface(o, T.TypeHandle);
}
public static TDelegate GetDelegateForFunctionPointer<TDelegate>(IntPtr ptr)
@@ -1288,13 +1287,12 @@ namespace System.Runtime.InteropServices
if (t.TypeHandle.IsGenericType() || t.TypeHandle.IsGenericTypeDefinition())
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t));
- bool isDelegateType = InteropExtensions.AreTypesAssignable(t.TypeHandle, typeof(MulticastDelegate).TypeHandle) ||
- InteropExtensions.AreTypesAssignable(t.TypeHandle, typeof(Delegate).TypeHandle);
+ bool isDelegateType = InteropExtensions.AreTypesAssignable(t.TypeHandle, typeof(Delegate).TypeHandle);
if (!isDelegateType)
throw new ArgumentException(SR.Arg_MustBeDelegateType, nameof(t));
- return MarshalAdapter.GetDelegateForFunctionPointer(ptr, t);
+ return McgMarshal.GetPInvokeDelegateForStub(ptr, t.TypeHandle);
}
//====================================================================
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs
index 599eca594..ecfe57bee 100644
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs
+++ b/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs
@@ -2,13 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-//
-// This file provides an implementation of the pieces of the Marshal class which are required by the Interop
-// API contract but are not provided by the version of Marshal which is part of the Redhawk test library.
-// This partial class is combined with the version from the Redhawk test library, in order to provide the
-// Marshal implementation for System.Private.CoreLib.
-//
-
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -36,7 +29,7 @@ namespace System.Runtime.InteropServices
//====================================================================
public static IntPtr /* IUnknown* */ GetIUnknownForObject(Object o)
{
- return MarshalImpl.GetIUnknownForObject(o);
+ return McgMarshal.ObjectToComInterface(o, InternalTypes.IUnknown);
}
//====================================================================
@@ -44,7 +37,7 @@ namespace System.Runtime.InteropServices
//====================================================================
public static Object GetObjectForIUnknown(IntPtr /* IUnknown* */ pUnk)
{
- object obj = MarshalImpl.GetObjectForIUnknown(pUnk);
+ object obj = McgMarshal.ComInterfaceToObject(pUnk, InternalTypes.IUnknown);
#if CORECLR
if (obj == null)
{
@@ -61,7 +54,13 @@ namespace System.Runtime.InteropServices
public static IntPtr /* IUnknown* */ GetComInterfaceForObject(Object o, Type T)
{
- IntPtr ptr = MarshalImpl.GetComInterfaceForObject(o, T);
+ if (o == null)
+ throw new ArgumentNullException(nameof(o));
+
+ if (T == null)
+ throw new ArgumentNullException(nameof(T));
+
+ IntPtr ptr = McgMarshal.ObjectToComInterface(o, T.TypeHandle);
#if CORECLR
if (ptr == default(IntPtr))
{
@@ -81,7 +80,7 @@ namespace System.Runtime.InteropServices
//====================================================================
public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t)
{
- Delegate dlg = MarshalImpl.GetDelegateForFunctionPointer(ptr, t);
+ Delegate dlg = McgMarshal.GetPInvokeDelegateForStub(ptr, t.TypeHandle);
#if CORECLR
if (dlg == null) // fall back to public marshal API
{
@@ -93,32 +92,42 @@ namespace System.Runtime.InteropServices
public static bool IsComObject(object o)
{
- return MarshalImpl.IsComObject(o);
+ if (o == null)
+ throw new ArgumentNullException(nameof(o));
+ return McgMarshal.IsComObject(o);
}
public static int ReleaseComObject(object o)
{
- return MarshalImpl.ReleaseComObject(o);
+ if (o == null)
+ throw new ArgumentNullException(nameof(o));
+ return McgMarshal.Release(o as __ComObject);
}
public static int FinalReleaseComObject(object o)
{
- return MarshalImpl.FinalReleaseComObject(o);
+ return McgMarshal.FinalReleaseComObject(o);
}
public static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv)
{
- return MarshalImpl.QueryInterface(pUnk, ref iid, out ppv);
+ int hr = 0;
+ ppv = McgMarshal.ComQueryInterfaceNoThrow(pUnk, ref iid, out hr);
+#if CORECLR
+ if (ppv == default(IntPtr))
+ return Marshal.QueryInterface(pUnk, ref iid, out ppv);
+#endif
+ return hr;
}
public static int AddRef(IntPtr pUnk)
{
- return MarshalImpl.AddRef(pUnk);
+ return McgMarshal.ComAddRef(pUnk);
}
public static int Release(IntPtr pUnk)
{
- return MarshalImpl.Release(pUnk);
+ return McgMarshal.ComRelease(pUnk);
}
}
#pragma warning restore 618
diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs b/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs
deleted file mode 100644
index 20f08a919..000000000
--- a/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.CompilerServices;
-
-namespace System.Runtime.InteropServices
-{
- internal static partial class MarshalImpl
- {
- public static IntPtr /* IUnknown* */ GetIUnknownForObject(Object o)
- {
- return McgMarshal.ObjectToComInterface(o, InternalTypes.IUnknown);
- }
-
- public static Object GetObjectForIUnknown(IntPtr /* IUnknown* */ pUnk)
- {
- return McgMarshal.ComInterfaceToObject(pUnk, InternalTypes.IUnknown);
- }
-
- public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t)
- {
- return McgMarshal.GetPInvokeDelegateForStub(ptr, t.TypeHandle);
- }
- public static IntPtr /* IUnknown* */ GetComInterfaceForObject(Object o, Type t)
- {
- if (o == null)
- throw new ArgumentNullException(nameof(o));
-
- if (t == null)
- throw new ArgumentNullException(nameof(t));
-
- return McgMarshal.ObjectToComInterface(o, t.TypeHandle);
- }
-
- public static bool IsComObject(object o)
- {
- if (o == null)
- throw new ArgumentNullException(nameof(o));
- return McgMarshal.IsComObject(o);
- }
- public static int ReleaseComObject(object o)
- {
- if (o == null)
- throw new ArgumentNullException(nameof(o));
- return McgMarshal.Release(o as __ComObject);
- }
-
- public static int FinalReleaseComObject(object o)
- {
- return McgMarshal.FinalReleaseComObject(o);
- }
-
- public static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv)
- {
- int hr = 0;
- ppv = McgMarshal.ComQueryInterfaceNoThrow(pUnk, ref iid, out hr);
-#if CORECLR
- if (ppv == default(IntPtr))
- return Marshal.QueryInterface(pUnk, ref iid, out ppv);
-#endif
- return hr;
- }
-
- public static int AddRef(IntPtr pUnk)
- {
- return McgMarshal.ComAddRef(pUnk);
- }
-
- public static int Release(IntPtr pUnk)
- {
- return McgMarshal.ComRelease(pUnk);
- }
- }
-}
diff --git a/src/System.Private.Interop/src/TypeForwarders.cs b/src/System.Private.Interop/src/TypeForwarders.cs
index 03271b59a..a056216a8 100644
--- a/src/System.Private.Interop/src/TypeForwarders.cs
+++ b/src/System.Private.Interop/src/TypeForwarders.cs
@@ -2,5 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.DefaultCharSetAttribute))]
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute))]
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.DllImportSearchPath))]
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.GuidAttribute))]
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.HandleRef))]
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.OptionalAttribute))]
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.PreserveSigAttribute))]
diff --git a/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs b/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs
index c023d5c06..6f9be707d 100644
--- a/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs
+++ b/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs
@@ -185,7 +185,7 @@ namespace System.Runtime.InteropServices
/// <param name="ex"></param>
/// <param name="isWinRTScenario"></param>
/// <returns></returns>
- internal static int GetHRForExceptionWithErrorPropogationNoThrow(Exception ex, bool isWinRTScenario)
+ internal static int GetHRForExceptionWithErrorPropagationNoThrow(Exception ex, bool isWinRTScenario)
{
int hr = ex.HResult;
diff --git a/src/System.Private.Jit/src/System.Private.Jit.csproj b/src/System.Private.Jit/src/System.Private.Jit.csproj
index 270c33469..662339fa5 100644
--- a/src/System.Private.Jit/src/System.Private.Jit.csproj
+++ b/src/System.Private.Jit/src/System.Private.Jit.csproj
@@ -26,7 +26,9 @@
<ReferencePath Include="$(AotPackageReferencePath)\System.Runtime.dll" />
<ReferencePath Include="$(AotPackageReferencePath)\System.Runtime.Extensions.dll" />
<ReferencePath Include="$(AotPackageReferencePath)\System.Private.Reflection.Metadata.Ecma335.dll" />
- <ProjectReference Include="..\..\System.Private.CoreLib\src\System.Private.CoreLib.csproj" />
+ <ProjectReference Include="..\..\System.Private.CoreLib\src\System.Private.CoreLib.csproj">
+ <Aliases>global,System_Private_CoreLib</Aliases>
+ </ProjectReference>
<ProjectReference Include="..\..\System.Private.Reflection.Metadata\src\System.Private.Reflection.Metadata.csproj" />
<ProjectReference Include="..\..\System.Private.TypeLoader\src\System.Private.TypeLoader.Experimental.csproj" />
<ProjectReference Include="..\..\System.Private.Interop\src\System.Private.Interop.csproj" />
diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs
index 392f6db9e..31de164ab 100644
--- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs
+++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs
@@ -89,7 +89,13 @@ namespace System.Reflection.Runtime.FieldInfos
{
get
{
- return this.FieldRuntimeType;
+ Type fieldType = _lazyFieldType;
+ if (fieldType == null)
+ {
+ _lazyFieldType = fieldType = this.FieldRuntimeType;
+ }
+
+ return fieldType;
}
}
@@ -292,6 +298,8 @@ namespace System.Reflection.Runtime.FieldInfos
private volatile FieldAccessor _lazyFieldAccessor = null;
+ private volatile Type _lazyFieldType = null;
+
private String _debugName;
}
}
diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs
index 90943008c..5d851fb09 100644
--- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs
+++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs
@@ -189,8 +189,14 @@ namespace System.Reflection.Runtime.General
object instantiatedAttribute = cad.Instantiate();
attributes.Add(instantiatedAttribute);
}
+
+ // This is here for desktop compatibility. ICustomAttribute.GetCustomAttributes() normally returns an array of the
+ // exact attribute type requested except in two cases: when the passed in type is an open type and when
+ // it is a value type. In these two cases, it returns an array of type Object[].
+ bool useObjectArray = actualElementType.ContainsGenericParameters || actualElementType.IsValueType;
int count = attributes.Count;
- object[] result = (object[])Array.CreateInstance(actualElementType, count);
+ object[] result = useObjectArray ? new object[count] : (object[])Array.CreateInstance(actualElementType, count);
+
attributes.CopyTo(result, 0);
return result;
}
diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs
index 98d338020..aa3f0dc82 100644
--- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs
+++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs
@@ -210,6 +210,14 @@ namespace System.Reflection.Runtime.TypeInfos
if (elementTypeHandle.IsNull())
return default(RuntimeTypeHandle);
+ // The check is here on purpose - one of the implementations of IsByRefLike contains a custom attribute
+ // search and those are very expensive from size on disk footprint perspective. We purposefully
+ // place this call in a path that won't be part of the executable image unless more advanced reflection services
+ // are also needed ("pay for play"). We really don't want a typeof() to push the app into requiring the full reflection
+ // stack to be compiled into the final executable.
+ if (elementType.IsByRefLike)
+ throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType));
+
RuntimeTypeHandle typeHandle;
if (!multiDim)
{
@@ -272,7 +280,7 @@ namespace System.Reflection.Runtime.TypeInfos
{
Debug.Assert(multiDim || rank == 1);
- if (elementType.IsByRef || elementType.IsByRefLike)
+ if (elementType.IsByRef)
throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType));
// We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result
diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs
index 7691902be..b8623f72c 100644
--- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs
+++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs
@@ -187,8 +187,14 @@ namespace System.Reflection.Runtime.PropertyInfos
ReflectionTrace.PropertyInfo_PropertyType(this);
#endif
- TypeContext typeContext = ContextTypeInfo.TypeContext;
- return PropertyTypeHandle.Resolve(typeContext);
+ Type propertyType = _lazyPropertyType;
+ if (propertyType == null)
+ {
+ TypeContext typeContext = ContextTypeInfo.TypeContext;
+ _lazyPropertyType = propertyType = PropertyTypeHandle.Resolve(typeContext);
+ }
+
+ return propertyType;
}
}
@@ -402,6 +408,8 @@ namespace System.Reflection.Runtime.PropertyInfos
private volatile ParameterInfo[] _lazyIndexParameters;
+ private volatile Type _lazyPropertyType;
+
private String _debugName;
}
}
diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs
index cd873da35..b583053d4 100644
--- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs
+++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs
@@ -1217,7 +1217,7 @@ namespace Internal.Reflection.Execution
NativeParser entryParser = new NativeParser(invokeMapReader, parserOffset);
- declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
+ RuntimeTypeHandle entryTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
// Hash table names / sigs are indirected through to the native layout info
MethodNameAndSignature nameAndSignature;
@@ -1236,8 +1236,9 @@ namespace Internal.Reflection.Execution
if (functionPointer != canonOriginalLdFtnResult)
return false;
- if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSignature, out methodHandle))
+ if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(entryTypeHandle, nameAndSignature, out methodHandle))
{
+ declaringTypeHandle = entryTypeHandle;
return true;
}
diff --git a/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs b/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
index 2d3bb5a0a..feb7dccf7 100644
--- a/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
+++ b/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
@@ -345,9 +345,9 @@ namespace Internal.StackTraceMetadata
/// <param name="namespaceQualified">When set to true, include namespace information</param>
private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, bool namespaceQualified)
{
+ // Stack trace metadata ignores the instantiation arguments of the type in the CLR
TypeInstantiationSignature typeInst = _metadataReader.GetTypeInstantiationSignature(typeInstHandle);
EmitTypeName(typeInst.GenericType, namespaceQualified);
- EmitGenericArguments(typeInst.GenericTypeArguments);
}
/// <summary>
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs
index 2cd5b9156..514cf8958 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs
@@ -110,7 +110,7 @@ namespace Internal.Runtime.TypeLoader
}
/// <summary>
- /// Try to look up field acccess info for given canon in metadata blobs for all available modules.
+ /// Try to look up field access info for given canon in metadata blobs for all available modules.
/// </summary>
/// <param name="metadataReader">Metadata reader for the declaring type</param>
/// <param name="declaringTypeHandle">Declaring type for the method</param>
diff --git a/src/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs b/src/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs
index 79baee7c9..a49f6ad78 100644
--- a/src/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs
+++ b/src/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs
@@ -63,5 +63,16 @@ namespace Internal.TypeSystem
return _methodDef.UnboxingStub;
}
}
+
+#if DEBUG
+ public override string ToString()
+ {
+ var sb = new System.Text.StringBuilder(_methodDef.ToString());
+ sb.Append('<');
+ sb.Append(_instantiation.ToString());
+ sb.Append('>');
+ return sb.ToString();
+ }
+#endif
}
}
diff --git a/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs b/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs
index 21f887fac..d70c8a4a9 100644
--- a/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs
+++ b/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs
@@ -17,5 +17,12 @@ namespace Internal.TypeSystem
return _typicalMethodDef.NameAndSignature;
}
}
+
+#if DEBUG
+ public override string ToString()
+ {
+ return OwningType.ToString() + "." + Name;
+ }
+#endif
}
}
diff --git a/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs b/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs
index 15bcb28bb..d9c688be6 100644
--- a/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs
+++ b/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs
@@ -94,7 +94,6 @@ namespace Internal.TypeSystem.NoMetadata
}
else
{
- Debug.Assert(this.HasNativeLayout);
// Parsing of the base type has not yet happened. Perform that part of native layout parsing
// just-in-time
TypeBuilderState state = GetOrCreateTypeBuilderState();
diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
index 06eb54493..1ba5ae861 100644
--- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
+++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
@@ -17,6 +17,10 @@
<OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TYPE_LOADER_IMPLEMENTATION;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition="'$(TYPE_LOADER_TRACE)' != ''">TYPE_LOADER_TRACE;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition="'$(GVM_RESOLUTION_TRACE)' != ''">GVM_RESOLUTION_TRACE;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition="'$(CCCONVERTER_TRACE)' != ''">CCCONVERTER_TRACE;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition="'$(GENERICS_FORCE_USG)' != ''">GENERICS_FORCE_USG;$(DefineConstants)</DefineConstants>
<IsDotNetFrameworkProductAssembly>true</IsDotNetFrameworkProductAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(EcmaMetadataSupport)' == 'true'">
@@ -33,13 +37,7 @@
<DefineConstants>SUPPORT_JIT;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsProjectNLibrary)' == 'true'">
- <DefineConstants>TYPE_LOADER_IMPLEMENTATION;$(DefineConstants)</DefineConstants>
<DefineConstants>FEATURE_INTERPRETER;$(DefineConstants)</DefineConstants>
- <DefineConstants Condition="'$(CCCONVERTER_TRACE)' != ''">CCCONVERTER_TRACE;$(DefineConstants)</DefineConstants>
- <DefineConstants Condition="'$(TYPE_LOADER_TRACE)' != ''">TYPE_LOADER_TRACE;$(DefineConstants)</DefineConstants>
- <DefineConstants Condition="'$(GENERICS_FORCE_USG)' != ''">GENERICS_FORCE_USG;$(DefineConstants)</DefineConstants>
- <DefineConstants Condition="'$(GVM_RESOLUTION_TRACE)' != ''">GVM_RESOLUTION_TRACE;$(DefineConstants)</DefineConstants>
- <DefineConstants>TYPE_SYSTEM_SINGLE_THREADED;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<!-- Setup the right references -->
<ItemGroup>
@@ -534,9 +532,6 @@
<Compile Include="..\..\Common\src\TypeSystem\Common\TypeDesc.ToString.cs">
<Link>Internal\TypeSystem\TypeDesc.ToString.cs</Link>
</Compile>
- <Compile Include="..\..\Common\src\TypeSystem\Common\MethodDesc.ToString.cs">
- <Link>Internal\TypeSystem\MethodDesc.ToString.cs</Link>
- </Compile>
<Compile Include="..\..\Common\src\TypeSystem\Common\FieldDesc.ToString.cs">
<Link>Internal\TypeSystem\FieldDesc.ToString.cs</Link>
</Compile>
diff --git a/src/Test.CoreLib/src/Test.CoreLib.csproj b/src/Test.CoreLib/src/Test.CoreLib.csproj
index a7588cad9..685a4fd63 100644
--- a/src/Test.CoreLib/src/Test.CoreLib.csproj
+++ b/src/Test.CoreLib/src/Test.CoreLib.csproj
@@ -84,7 +84,7 @@
</ItemGroup>
<ItemGroup Condition="'$(InPlaceRuntime)' == 'true'">
<Compile Condition="'$(Platform)'!='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Full\AsmOffsets.cs" />
- <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsets.cs" />
+ <Compile Condition="'$(Platform)'=='wasm'" Include="$(BaseIntermediateOutputPath)\Native\$(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration)\Runtime\Portable\AsmOffsetsPortable.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Common\src\Internal\NativeFormat\NativeFormatReader.Primitives.cs">
diff --git a/src/dirs.proj b/src/dirs.proj
index 2356eef0d..bc9ce5cad 100644
--- a/src/dirs.proj
+++ b/src/dirs.proj
@@ -6,17 +6,15 @@
<ExcludeProjects Condition="'$(OSEnvironment)'!='Windows_NT'" Include="**\TypeSystem.Tests.csproj" />
<ExcludeProjects Condition="'$(OSEnvironment)'!='Windows_NT'" Include="**\ILCompiler.MetadataTransform.Tests.csproj" />
- <!-- These projects target netcoreapp2.0, so they are disabled until #3682 is done -->
- <ExcludeProjects Include="**\ILVerify.csproj" />
- <ExcludeProjects Include="**\ILVerify.Tests.csproj" />
-
<Project Include="AotPackageReference\AotPackageReference.depproj" />
<Project Include="Framework\Framework.depproj" />
<Project Include="Framework\Framework-uapaot.depproj" />
<Project Include="Framework\Framework-native.depproj" Condition="$(TargetsUnix) and '$(Platform)' != 'wasm'" />
- <Project Include="ILCompiler\**\*.depproj" />
+ <Project Include="ILCompiler\RyuJIT\RyuJIT.depproj" />
+ <Project Include="ILCompiler\ObjectWriter\ObjectWriter.depproj" />
+ <Project Include="ILCompiler.WebAssembly\src\libLLVMdep.depproj" />
<Project Include="*\src\*.csproj" Exclude="@(ExcludeProjects)" />
<Project Include="*\src\*.vbproj" Condition="'$(IncludeVbProjects)'!='false'" Exclude="@(ExcludeProjects)" />
diff --git a/tests/CoreCLR.issues.targets b/tests/CoreCLR.issues.targets
index cababdc89..d7b9ad486 100644
--- a/tests/CoreCLR.issues.targets
+++ b/tests/CoreCLR.issues.targets
@@ -2,17 +2,6 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup Condition="'$(XunitTestBinBase)' != ''">
- <!-- Tests that fail because they need update from CoreCLR -->
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_cs_ro\intrinsic_cs_ro.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_nonf_il_r\intrinsic_nonf_il_r.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_cs_d\intrinsic_cs_d.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_nonf_il_d\intrinsic_nonf_il_d.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_cs_do\intrinsic_cs_do.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Directed\intrinsic\pow\pow1\pow1.*" />
- <ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\NaN\intrinsic_cs_r\intrinsic_cs_r.*" />
- <ExcludeList Include="$(XunitTestBinBase)\GC\Scenarios\Samples\gc\gc.*" />
- <ExcludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedObject\PinnedObject.*" />
-
<!-- Infinite generic expansion -->
<!-- https://github.com/dotnet/corert/issues/363 -->
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\inlining\bug505642\test\test.*" />
@@ -51,6 +40,13 @@
<!-- System.Runtime.CompilerServices.Unsafe -->
<ExcludeList Include="$(XunitTestBinBase)\JIT\opt\Inline\tests\UnsafeBlockCopy\UnsafeBlockCopy.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_16254\GitHub_16254\GitHub_16254.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_16210\GitHub_16210_2\GitHub_16210_2.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_16210\GitHub_16210_3\GitHub_16210_3.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_15237\GitHub_15237\GitHub_15237.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_13568\GitHub_13568\GitHub_13568.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_11804\GitHub_11804\GitHub_11804.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\BenchmarksGame\mandelbrot\mandelbrot-7\mandelbrot-7.*" />
<!-- These depend on things outside of the .NET Core profile. Need test fixes. -->
<ExcludeList Include="$(XunitTestBinBase)\Interop\NativeCallable\NativeCallableTest\NativeCallableTest.*" />
@@ -224,6 +220,9 @@
<ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\SIMD\RayTracer\RayTracer\RayTracer.*" />
<ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\V8\DeltaBlue\DeltaBlue\DeltaBlue.*" />
<ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\V8\Richards\Richards\Richards.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\Span\SpanBench\SpanBench.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\Span\Indexer\Indexer.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\SIMD\SeekUnroll\SeekUnroll\SeekUnroll.*" />
<!-- getTailCallCopyArgsThunk -->
<!-- https://github.com/dotnet/corert/issues/1683 -->
@@ -418,10 +417,8 @@
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\Ambiguous\Ambiguous.*" />
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\SameMethodImpl\CollapsedInterfaces\HelloWorld\HelloWorld.*" />
- <!-- Ambiguous virtual overloads -->
- <!-- https://github.com/dotnet/corert/issues/190 -->
- <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\CollapsedMethods\InterfaceImplementation\HelloWorld\HelloWorld.*" />
- <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\CollapsedMethods\Override\HelloWorld\HelloWorld.*" />
+ <!-- Complex constrained method calls in shared generic code -->
+ <!-- https://github.com/dotnet/corert/issues/2526 -->
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_568786\4_Misc\Variance2\Variance2.*" />
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_568786\4_Misc\SealedTypes\SealedTypes.*" />
@@ -609,6 +606,328 @@
<!-- https://github.com/dotnet/corert/issues/2368 -->
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\MethodImpl\override_override1\override_override1.*" />
+ <!-- GVM analysis in multi-module build -->
+ <!-- https://github.com/dotnet/corert/issues/5408 -->
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\BenchmarksGame\fasta\fasta-1\fasta-1.*" />
+
+ <!-- Obsolete Marshal APIs -->
+ <ExcludeList Include="$(XunitTestBinBase)\Interop\MarshalAPI\ReadWrite\ReadWriteObject\ReadWriteObject.*" />
+
+ <!-- Default interface methods -->
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\constrainedcall\constrainedcall\constrainedcall.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\diamondshape\diamondshape_d\diamondshape_d.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\diamondshape\diamondshape_r\diamondshape_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\genericmethods\genericmethods\genericmethods.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\methodimpl\methodimpl\methodimpl.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\sharedgenerics\sharedgenerics_d\sharedgenerics_d.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\sharedgenerics\sharedgenerics_r\sharedgenerics_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\simple\simple\simple.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\DefaultInterfaceMethods\valuetypes\valuetypes\valuetypes.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\reflection\DefaultInterfaceMethods\Emit\Emit.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\reflection\DefaultInterfaceMethods\GetInterfaceMapConsumer\GetInterfaceMapConsumer.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\reflection\DefaultInterfaceMethods\InvokeConsumer\InvokeConsumer.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\Regressions\coreclr\15650\interfacecctor\interfacecctor.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\baseservices\compilerservices\RuntimeFeature\DefaultImplementationsOfInterfaces\DefaultImplementationsOfInterfaces.*" />
+
+ <!-- Hardware intrinsics -->
+ <ExcludeList Include="$(XunitTestBinBase)\Interop\StructPacking\StructPacking\StructPacking.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\Arm64\Simd\Simd.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Avx_r\Avx_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Avx_ro\Avx_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\HorizontalAdd_r\HorizontalAdd_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\HorizontalAdd_ro\HorizontalAdd_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\HorizontalSubtract_r\HorizontalSubtract_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\HorizontalSubtract_ro\HorizontalSubtract_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadAlignedVector256_r\LoadAlignedVector256_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadAlignedVector256_ro\LoadAlignedVector256_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadDquVector256_r\LoadDquVector256_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadDquVector256_ro\LoadDquVector256_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadVector256_r\LoadVector256_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\LoadVector256_ro\LoadVector256_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Sqrt_r\Sqrt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Sqrt_ro\Sqrt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\StoreAlignedNonTemporal_r\StoreAlignedNonTemporal_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\StoreAlignedNonTemporal_ro\StoreAlignedNonTemporal_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\StoreAligned_r\StoreAligned_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\StoreAligned_ro\StoreAligned_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Store_r\Store_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\Store_ro\Store_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\UnpackHigh_r\UnpackHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\UnpackHigh_ro\UnpackHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\UnpackLow_r\UnpackLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx\UnpackLow_ro\UnpackLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\AddSaturate_r\AddSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\AddSaturate_ro\AddSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\Avx2_r\Avx2_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\Avx2_ro\Avx2_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\HorizontalAdd_r\HorizontalAdd_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\HorizontalAdd_ro\HorizontalAdd_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\HorizontalSubtract_r\HorizontalSubtract_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\HorizontalSubtract_ro\HorizontalSubtract_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\LoadAlignedVector256NonTemporal_r\LoadAlignedVector256NonTemporal_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\LoadAlignedVector256NonTemporal_ro\LoadAlignedVector256NonTemporal_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\Multiply_r\Multiply_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\Multiply_ro\Multiply_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\SubtractSaturate_r\SubtractSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\SubtractSaturate_ro\SubtractSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\UnpackHigh_r\UnpackHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\UnpackHigh_ro\UnpackHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\UnpackLow_r\UnpackLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Avx2\UnpackLow_ro\UnpackLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\IsSupported_r\IsSupported_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\IsSupported_ro\IsSupported_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Lzcnt\Lzcnt_r\Lzcnt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Lzcnt\Lzcnt_ro\Lzcnt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Popcnt\Popcnt_r\Popcnt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Popcnt\Popcnt_ro\Popcnt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertScalarToVector128Single_r\ConvertScalarToVector128Single_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertScalarToVector128Single_ro\ConvertScalarToVector128Single_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt32WithTruncation_r\ConvertToInt32WithTruncation_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt32WithTruncation_ro\ConvertToInt32WithTruncation_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt32_r\ConvertToInt32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt32_ro\ConvertToInt32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt64WithTruncation_r\ConvertToInt64WithTruncation_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt64WithTruncation_ro\ConvertToInt64WithTruncation_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt64_r\ConvertToInt64_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToInt64_ro\ConvertToInt64_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToSingle_r\ConvertToSingle_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ConvertToSingle_ro\ConvertToSingle_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadAlignedVector128_r\LoadAlignedVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadAlignedVector128_ro\LoadAlignedVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadHigh_r\LoadHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadHigh_ro\LoadHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadLow_r\LoadLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadLow_ro\LoadLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadScalarVector128_r\LoadScalarVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadScalarVector128_ro\LoadScalarVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadVector128_r\LoadVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\LoadVector128_ro\LoadVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveHighToLow_r\MoveHighToLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveHighToLow_ro\MoveHighToLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveLowToHigh_r\MoveLowToHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveLowToHigh_ro\MoveLowToHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveMask_r\MoveMask_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveMask_ro\MoveMask_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveScalar_r\MoveScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\MoveScalar_ro\MoveScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Prefetch_r\Prefetch_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Prefetch_ro\Prefetch_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalScalar_r\ReciprocalScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalScalar_ro\ReciprocalScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalSqrtScalar_r\ReciprocalSqrtScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalSqrtScalar_ro\ReciprocalSqrtScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalSqrt_r\ReciprocalSqrt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\ReciprocalSqrt_ro\ReciprocalSqrt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Reciprocal_r\Reciprocal_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Reciprocal_ro\Reciprocal_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetAllVector128_r\SetAllVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetAllVector128_ro\SetAllVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetScalarVector128_r\SetScalarVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetScalarVector128_ro\SetScalarVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetVector128_r\SetVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetVector128_ro\SetVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetZeroVector128_r\SetZeroVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SetZeroVector128_ro\SetZeroVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Shuffle_r\Shuffle_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Shuffle_ro\Shuffle_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SqrtScalar_r\SqrtScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\SqrtScalar_ro\SqrtScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Sqrt_r\Sqrt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Sqrt_ro\Sqrt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Sse_r\Sse_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Sse_ro\Sse_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StaticCast_r\StaticCast_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StaticCast_ro\StaticCast_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreAlignedNonTemporal_r\StoreAlignedNonTemporal_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreAlignedNonTemporal_ro\StoreAlignedNonTemporal_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreAligned_r\StoreAligned_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreAligned_ro\StoreAligned_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreFence_r\StoreFence_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreFence_ro\StoreFence_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreHigh_r\StoreHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreHigh_ro\StoreHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreLow_r\StoreLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreLow_ro\StoreLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreScalar_r\StoreScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\StoreScalar_ro\StoreScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Store_r\Store_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\Store_ro\Store_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\UnpackHigh_r\UnpackHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\UnpackHigh_ro\UnpackHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\UnpackLow_r\UnpackLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse\UnpackLow_ro\UnpackLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\AddSaturate_r\AddSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\AddSaturate_ro\AddSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\AddScalar_r\AddScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\AddScalar_ro\AddScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Average_r\Average_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Average_ro\Average_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualOrderedScalar_r\CompareEqualOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualOrderedScalar_ro\CompareEqualOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualScalar_r\CompareEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualScalar_ro\CompareEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualUnorderedScalar_r\CompareEqualUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareEqualUnorderedScalar_ro\CompareEqualUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrderedScalar_r\CompareGreaterThanOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrderedScalar_ro\CompareGreaterThanOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualOrderedScalar_r\CompareGreaterThanOrEqualOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualOrderedScalar_ro\CompareGreaterThanOrEqualOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualScalar_r\CompareGreaterThanOrEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualScalar_ro\CompareGreaterThanOrEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualUnorderedScalar_r\CompareGreaterThanOrEqualUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanOrEqualUnorderedScalar_ro\CompareGreaterThanOrEqualUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanScalar_r\CompareGreaterThanScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanScalar_ro\CompareGreaterThanScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanUnorderedScalar_r\CompareGreaterThanUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareGreaterThanUnorderedScalar_ro\CompareGreaterThanUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrderedScalar_r\CompareLessThanOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrderedScalar_ro\CompareLessThanOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualOrderedScalar_r\CompareLessThanOrEqualOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualOrderedScalar_ro\CompareLessThanOrEqualOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualScalar_r\CompareLessThanOrEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualScalar_ro\CompareLessThanOrEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualUnorderedScalar_r\CompareLessThanOrEqualUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanOrEqualUnorderedScalar_ro\CompareLessThanOrEqualUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanScalar_r\CompareLessThanScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanScalar_ro\CompareLessThanScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanUnorderedScalar_r\CompareLessThanUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareLessThanUnorderedScalar_ro\CompareLessThanUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualOrderedScalar_r\CompareNotEqualOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualOrderedScalar_ro\CompareNotEqualOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualScalar_r\CompareNotEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualScalar_ro\CompareNotEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualUnorderedScalar_r\CompareNotEqualUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotEqualUnorderedScalar_ro\CompareNotEqualUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotGreaterThanOrEqualScalar_r\CompareNotGreaterThanOrEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotGreaterThanOrEqualScalar_ro\CompareNotGreaterThanOrEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotGreaterThanScalar_r\CompareNotGreaterThanScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotGreaterThanScalar_ro\CompareNotGreaterThanScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotLessThanOrEqualScalar_r\CompareNotLessThanOrEqualScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotLessThanOrEqualScalar_ro\CompareNotLessThanOrEqualScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotLessThanScalar_r\CompareNotLessThanScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareNotLessThanScalar_ro\CompareNotLessThanScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareOrderedScalar_r\CompareOrderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareOrderedScalar_ro\CompareOrderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareUnorderedScalar_r\CompareUnorderedScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\CompareUnorderedScalar_ro\CompareUnorderedScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Double_r\ConvertScalarToVector128Double_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Double_ro\ConvertScalarToVector128Double_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Int32_r\ConvertScalarToVector128Int32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Int32_ro\ConvertScalarToVector128Int32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Int64_r\ConvertScalarToVector128Int64_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Int64_ro\ConvertScalarToVector128Int64_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Single_r\ConvertScalarToVector128Single_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128Single_ro\ConvertScalarToVector128Single_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128UInt32_r\ConvertScalarToVector128UInt32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128UInt32_ro\ConvertScalarToVector128UInt32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128UInt64_r\ConvertScalarToVector128UInt64_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertScalarToVector128UInt64_ro\ConvertScalarToVector128UInt64_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToDouble_r\ConvertToDouble_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToDouble_ro\ConvertToDouble_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt32WithTruncation_r\ConvertToInt32WithTruncation_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt32WithTruncation_ro\ConvertToInt32WithTruncation_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt32_r\ConvertToInt32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt32_ro\ConvertToInt32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt64WithTruncation_r\ConvertToInt64WithTruncation_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt64WithTruncation_ro\ConvertToInt64WithTruncation_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt64_r\ConvertToInt64_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToInt64_ro\ConvertToInt64_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToUInt32_r\ConvertToUInt32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToUInt32_ro\ConvertToUInt32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToUInt64_r\ConvertToUInt64_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToUInt64_ro\ConvertToUInt64_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Double_r\ConvertToVector128Double_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Double_ro\ConvertToVector128Double_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Int32WithTruncation_r\ConvertToVector128Int32WithTruncation_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Int32WithTruncation_ro\ConvertToVector128Int32WithTruncation_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Int32_r\ConvertToVector128Int32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Int32_ro\ConvertToVector128Int32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Single_r\ConvertToVector128Single_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\ConvertToVector128Single_ro\ConvertToVector128Single_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\DivideScalar_r\DivideScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\DivideScalar_ro\DivideScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadAlignedVector128_r\LoadAlignedVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadAlignedVector128_ro\LoadAlignedVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadFence_r\LoadFence_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadFence_ro\LoadFence_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadScalarVector128_r\LoadScalarVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadScalarVector128_ro\LoadScalarVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadVector128_r\LoadVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\LoadVector128_ro\LoadVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MaxScalar_r\MaxScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MaxScalar_ro\MaxScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MemoryFence_r\MemoryFence_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MemoryFence_ro\MemoryFence_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MinScalar_r\MinScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MinScalar_ro\MinScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MoveMask_r\MoveMask_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MoveMask_ro\MoveMask_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MoveScalar_r\MoveScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MoveScalar_ro\MoveScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyHigh_r\MultiplyHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyHigh_ro\MultiplyHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyHorizontalAdd_r\MultiplyHorizontalAdd_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyHorizontalAdd_ro\MultiplyHorizontalAdd_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyLow_r\MultiplyLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyLow_ro\MultiplyLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyScalar_r\MultiplyScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\MultiplyScalar_ro\MultiplyScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Multiply_r\Multiply_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Multiply_ro\Multiply_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\PackSignedSaturate_r\PackSignedSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\PackSignedSaturate_ro\PackSignedSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\PackUnsignedSaturate_r\PackUnsignedSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\PackUnsignedSaturate_ro\PackUnsignedSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SetZeroVector128_r\SetZeroVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SetZeroVector128_ro\SetZeroVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SqrtScalar_r\SqrtScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SqrtScalar_ro\SqrtScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Sqrt_r\Sqrt_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Sqrt_ro\Sqrt_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Sse2_r\Sse2_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Sse2_ro\Sse2_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreAlignedNonTemporal_r\StoreAlignedNonTemporal_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreAlignedNonTemporal_ro\StoreAlignedNonTemporal_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreAligned_r\StoreAligned_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreAligned_ro\StoreAligned_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreHigh_r\StoreHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreHigh_ro\StoreHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreLow_r\StoreLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreLow_ro\StoreLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreScalar_r\StoreScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\StoreScalar_ro\StoreScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Store_r\Store_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\Store_ro\Store_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SubtractSaturate_r\SubtractSaturate_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SubtractSaturate_ro\SubtractSaturate_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SubtractScalar_r\SubtractScalar_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SubtractScalar_ro\SubtractScalar_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SumAbsoluteDifferences_r\SumAbsoluteDifferences_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\SumAbsoluteDifferences_ro\SumAbsoluteDifferences_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\UnpackHigh_r\UnpackHigh_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\UnpackHigh_ro\UnpackHigh_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\UnpackLow_r\UnpackLow_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse2\UnpackLow_ro\UnpackLow_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse3\LoadAndDuplicateToVector128_r\LoadAndDuplicateToVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse3\LoadAndDuplicateToVector128_ro\LoadAndDuplicateToVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse3\LoadDquVector128_r\LoadDquVector128_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse3\LoadDquVector128_ro\LoadDquVector128_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse41\Multiply_r\Multiply_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse41\Multiply_ro\Multiply_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse41\Sse41_r\Sse41_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse41\Sse41_ro\Sse41_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse42\Crc32_r\Crc32_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse42\Crc32_ro\Crc32_ro.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse42\Sse42_r\Sse42_r.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\HardwareIntrinsics\X86\Sse42\Sse42_ro\Sse42_ro.*" />
+
+ <!-- Event tracing -->
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\eventactivityidcontrol\eventactivityidcontrol\eventactivityidcontrol.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\eventlistener\eventlistener\eventlistener.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\eventpipesmoke\eventpipesmoke\eventpipesmoke.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\tracevalidation\inducedgc\inducedgc\inducedgc.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\tracing\tracevalidation\jittingstarted\JittingStarted\JittingStarted.*" />
+
<!-- NetStandard2: System.Delegate System.Delegate.CreateDelegate -->
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_720779\dev10_720779\dev10_720779.*" />
@@ -635,6 +954,7 @@
<!-- Test infra problem -->
<ExcludeList Include="$(XunitTestBinBase)\baseservices\threading\paramthreadstart\ThreadStartBool_1\ThreadStartBool_1.*" />
<ExcludeList Include="$(XunitTestBinBase)\baseservices\threading\paramthreadstart\ThreadStartBool\ThreadStartBool.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\DevDiv_461649\DevDiv_461649\DevDiv_461649.*" />
<!-- ILDASM tests, not runtime tests -->
<ExcludeList Include="$(XunitTestBinBase)\JIT\BBT\Scenario4\Not-Int32\Not-Int32.*" />
@@ -652,8 +972,8 @@
<!-- Crossgen tests, not runtime tests -->
<ExcludeList Include="$(XunitTestBinBase)\readytorun\genericsload\callgenericctor\callgenericctor.*" />
<ExcludeList Include="$(XunitTestBinBase)\readytorun\genericsload\usegenericfield\usegenericfield.*" />
- <ExcludeList Include="$(XunitTestBinBase)\readytorun\mainv1\mainv1.*" />
- <ExcludeList Include="$(XunitTestBinBase)\readytorun\mainv2\mainv2.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\readytorun\tests\mainv1\mainv1.*" />
+ <ExcludeList Include="$(XunitTestBinBase)\readytorun\tests\mainv2\mainv2.*" />
<ExcludeList Include="$(XunitTestBinBase)\readytorun\generics\generics.*" />
<!-- Superpmi tests, not runtime tests -->
diff --git a/tests/CoreCLR/build-and-run-test.cmd b/tests/CoreCLR/build-and-run-test.cmd
index 9b0feacc5..4e8b7764b 100644
--- a/tests/CoreCLR/build-and-run-test.cmd
+++ b/tests/CoreCLR/build-and-run-test.cmd
@@ -25,7 +25,7 @@ copy /Y %~dp0\Test.csproj %TestFolder%
:: so override if we're doing a 64-bit test run
::
if "%CoreRT_BuildArch%" == "x64" (
- call "%VS140COMNTOOLS%\..\..\VC\bin\amd64\vcvars64.bat"
+ call "%VS150COMNTOOLS%\..\..\VC\Auxiliary\Build\vcvarsall.bat" x64
)
echo msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /p:DisableFrameworkLibGeneration=true %TestFolder%\Test.csproj
diff --git a/tests/CoreCLRTestsURL.txt b/tests/CoreCLRTestsURL.txt
index 388aabee4..c5fd5d3cd 100644
--- a/tests/CoreCLRTestsURL.txt
+++ b/tests/CoreCLRTestsURL.txt
@@ -1 +1 @@
-https://ci.dot.net/job/dotnet_coreclr/job/master/job/debug_windows_nt_prtest/4/artifact/bin/tests/tests.zip \ No newline at end of file
+https://ci.dot.net/job/dotnet_coreclr/job/master/job/x64_checked_windows_nt_innerloop_prtest/839/artifact/bin/tests/tests.zip \ No newline at end of file
diff --git a/tests/Top200.CoreCLR.issues.targets b/tests/Top200.CoreCLR.issues.targets
index 801149849..a2b5551c5 100644
--- a/tests/Top200.CoreCLR.issues.targets
+++ b/tests/Top200.CoreCLR.issues.targets
@@ -7,211 +7,132 @@
<Project DefaultTargets = "GetListOfTestCmds"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup Condition="'$(XunitTestBinBase)' != ''">
- <IncludeList Include="$(XunitTestBinBase)\GC\Features\PartialCompaction\eco1\eco1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Features\PartialCompaction\partialcompactiontest\partialcompactiontest.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Features\PartialCompaction\partialcompactionwloh\partialcompactionwloh.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\vararystress\vararystress.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\baseservices\threading\DeadThreads\DeadThreads\DeadThreads.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Exceptions\Finalization\Finalizer\Finalizer.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\Collect0\Collect0.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\Collect1\Collect1.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\CollectionCountTest\CollectionCountTest.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\Collect_fail\Collect_fail.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\Collect_neg\Collect_neg.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\GetGeneration\GetGeneration.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\GetGenerationWR\GetGenerationWR.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GC\GetGeneration_fail\GetGeneration_fail.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\AddrOfPinnedObject_neg\AddrOfPinnedObject_neg.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\Alloc_neg2\Alloc_neg2.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\Casting\Casting.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\Free_neg\Free_neg.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\PinObj_neg\PinObj_neg.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\Target_neg\Target_neg.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandle\ToFromIntPtr\ToFromIntPtr.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandleCollector\Count\Count.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCHandleCollector\NegTests\NegTests.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\GCSettings\InputValidation\InputValidation.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\NoGCRegion\NoGC\NoGC.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\WeakReference\Finalize2\Finalize2.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\WeakReference\multipleWRs\multipleWRs.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\API\WeakReference\multipleWRs_1\multipleWRs_1.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Coverage\LargeObjectAlloc\LargeObjectAlloc.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Coverage\LargeObjectAlloc2\LargeObjectAlloc2.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedCollect\PinnedCollect.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedHandle\PinnedHandle.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedMany\PinnedMany.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedMultiple\PinnedMultiple.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Features\Pinning\PinningOther\PinnedObject\PinnedObject.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\LargeMemory\API\gc\gettotalmemory\gettotalmemory.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\LargeMemory\Regressions\largearraytest\largearraytest.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\LargeMemory\Regressions\pressureoverflow\pressureoverflow.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta1\149926\149926\149926.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta1\289745\289745\289745.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta1\289745\302560\302560.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta2\426480\426480\426480.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta2\445488\445488\445488.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta2\452950\452950\452950.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-beta2\460373\460373\460373.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Regressions\v2.0-rtm\494226\494226\494226.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\BinTree\thdtree\thdtree.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\BinTree\thdtreegrowingobj\thdtreegrowingobj.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\BinTree\thdtreelivingobj\thdtreelivingobj.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\arrcpy\arrcpy.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\gcvariant\gcvariant.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\gcvariant2\gcvariant2.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\gcvariant3\gcvariant3.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\gcvariant4\gcvariant4.cmd" />
<IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\variantint\variantint.cmd" />
<IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Boxing\variantlinklist\variantlinklist.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\FragMan\fragman\fragman.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCBase1\gc_base1\gc_base1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCBase1\gc_base1_1\gc_base1_1.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\FinalizeTimeout\FinalizeTimeout\FinalizeTimeout.cmd" />
<IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCBench\gcbench\gcbench.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_287\GCSimulator_287.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_288\GCSimulator_288.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_289\GCSimulator_289.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_29\GCSimulator_29.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_290\GCSimulator_290.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_291\GCSimulator_291.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_292\GCSimulator_292.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_293\GCSimulator_293.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCSimulator\GCSimulator_294\GCSimulator_294.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\GCStress\gcstress\gcstress.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\LeakWheel\leakwheel\leakwheel.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\MinLeakGen\minleakgen\minleakgen.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\muldimjagary\muldimjagary\muldimjagary.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\NDPin\ndpin\ndpin.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\NDPin\ndpinfinal\ndpinfinal.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\RanCollect\rancollect\rancollect.cmd" />
<IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Resurrection\continue\continue.cmd" />
<IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\Rootmem\rootmem\rootmem.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\SingLinkList\singlinkstay\singlinkstay.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\MarshalAPI\Copy\CopyByteArray\CopyByteArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\MarshalAPI\Copy\CopyCharArray\CopyCharArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\MarshalAPI\Copy\CopyDoubleArray\CopyDoubleArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\MarshalAPI\Copy\CopyInt16Array\CopyInt16Array.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\ArrayMarshalling\BoolArray\MarshalBoolArrayTest\MarshalBoolArrayTest.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\PrimitiveMarshalling\Bool\BoolTest\BoolTest.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\SizeConst\SizeConstTest\SizeConstTest.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Interop\PrimitiveMarshalling\UIntPtr\PInvokeUIntPtrTest\PInvokeUIntPtrTest.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\addref\addref.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\And1\And1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AndRef\AndRef.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Args4\Args4.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Args5\Args5.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AsgAdd1\AsgAdd1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AsgAnd1\AsgAnd1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AsgOr1\AsgOr1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AsgSub1\AsgSub1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\AsgXor1\AsgXor1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\BinaryRMW\BinaryRMW.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Call1\Call1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\CnsBool\CnsBool.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\CnsLng1\CnsLng1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblAdd\DblAdd.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblAddConst\DblAddConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblArea\DblArea.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblArray\DblArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblAvg2\DblAvg2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblAvg6\DblAvg6.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblCall1\DblCall1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblCall2\DblCall2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblDist\DblDist.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblDiv\DblDiv.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblDivConst\DblDivConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblFillArray\DblFillArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblMul\DblMul.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblMulConst\DblMulConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblNeg\DblNeg.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblRem\DblRem.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblRoots\DblRoots.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblSub\DblSub.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblSubConst\DblSubConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\DblVar\DblVar.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\div1\div1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\divref\divref.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Eq1\Eq1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FactorialRec\FactorialRec.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FibLoop\FibLoop.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FiboRec\FiboRec.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPAdd\FPAdd.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPAddConst\FPAddConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPArea\FPArea.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPArray\FPArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPAvg2\FPAvg2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPAvg6\FPAvg6.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPCall1\FPCall1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPCall2\FPCall2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPConvDbl2Lng\FPConvDbl2Lng.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPConvF2F\FPConvF2F.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPConvF2I\FPConvF2I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPConvF2Lng\FPConvF2Lng.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPConvI2F\FPConvI2F.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPDist\FPDist.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPDiv\FPDiv.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPDivConst\FPDivConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPError\FPError.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPFillArray\FPFillArray.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPMath\FPMath.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPMul\FPMul.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPMulConst\FPMulConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPNeg\FPNeg.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPRem\FPRem.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPRoots\FPRoots.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPSmall\FPSmall.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPSub\FPSub.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPSubConst\FPSubConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\FPVar\FPVar.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Gcd\Gcd.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Ge1\Ge1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Gt1\Gt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Ind1\Ind1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\InitObj\InitObj.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\InstanceCalls\InstanceCalls.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\IntArraySum\IntArraySum.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\IntConv\IntConv.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Jmp1\Jmp1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrue1\JTrue1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueEqDbl\JTrueEqDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueEqFP\JTrueEqFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueEqInt1\JTrueEqInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGeDbl\JTrueGeDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGeFP\JTrueGeFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGeInt1\JTrueGeInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGtDbl\JTrueGtDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGtFP\JTrueGtFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueGtInt1\JTrueGtInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLeDbl\JTrueLeDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLeFP\JTrueLeFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLeInt1\JTrueLeInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLtDbl\JTrueLtDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLtFP\JTrueLtFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueLtInt1\JTrueLtInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueNeDbl\JTrueNeDbl.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueNeFP\JTrueNeFP.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\JTrueNeInt1\JTrueNeInt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Le1\Le1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\LeftShift\LeftShift.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\LngConv\LngConv.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Localloc\Localloc.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\LongArgsAndReturn\LongArgsAndReturn.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Lt1\Lt1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\mul1\mul1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\mul2\mul2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\mul3\mul3.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\mul4\mul4.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Ne1\Ne1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\NegRMW\NegRMW.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\NestedCall\NestedCall.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\NotAndNeg\NotAndNeg.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\NotRMW\NotRMW.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\ObjAlloc\ObjAlloc.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\OpMembersOfStructLocal\OpMembersOfStructLocal.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Or1\Or1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\OrRef\OrRef.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\rem1\rem1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\RightShiftRef\RightShiftRef.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Rotate\Rotate.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\StaticCalls\StaticCalls.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\StaticValueField\StaticValueField.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\struct16args\struct16args.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\StructFldAddr\StructFldAddr.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\StructInstMethod\StructInstMethod.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Sub1\Sub1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\SubRef\SubRef.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Swap\Swap.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Switch\Switch.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\UDivConst\UDivConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\UModConst\UModConst.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Unbox\Unbox.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\Xor1\Xor1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\CodeGenBringUpTests\XorRef\XorRef.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\ExcepFilters\excepobj\excepobj\excepobj.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\ExcepFilters\fault\fault\fault.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\ExcepFilters\mixed\mixed\mixed.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\FaultHandlers\Nesting\Nesting\Nesting.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\nullabletypes\isinst_d\isinst_d.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Directed\nullabletypes\isinst_do\isinst_do.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\DevDiv_142976\DevDiv_142976\DevDiv_142976.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\JIT\jit64\localloc\eh\eh01_dynamic\eh01_dynamic.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\StringCreator\stringcreator\stringcreator.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\THDChaos\thdchaos\thdchaos.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\GC\Scenarios\THDList\thdlist\thdlist.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\Inline\tests\calli\calli.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\Inline\tests\InlineThrow\InlineThrow.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\Inline\tests\mathfunc\mathfunc.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\LocAlloc\inloop\inloop.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\LocAlloc\localloc\localloc.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\perf\doublealign\Arrays\Arrays.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\Types\Equality\Equality.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\opt\virtualstubdispatch\bigvtbl\bigvtbl_cs_ro\bigvtbl_cs_ro.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\AbsGeneric_r\AbsGeneric_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\AbsSqrt_r\AbsSqrt_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\AddingSequence_r\AddingSequence_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\BitwiseOperations_r\BitwiseOperations_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\BoxUnbox_ro\BoxUnbox_ro.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\CircleInConvex_r\CircleInConvex_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\CreateGeneric_r\CreateGeneric_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\CtorFromArray_r\CtorFromArray_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\Ctors_r\Ctors_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\DivSignedUnsignedTest_r\DivSignedUnsignedTest_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\Matrix4x4_r\Matrix4x4_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\MinMax_r\MinMax_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\Mul_ro\Mul_ro.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\SqrtGeneric_r\SqrtGeneric_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\Vector3_r\Vector3_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\VectorAbs_r\VectorAbs_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\JIT\SIMD\VectorArrayInit_r\VectorArrayInit_r.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase01\AbstractBase01.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase02\AbstractBase02.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase03\AbstractBase03.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase05\AbstractBase05.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase06\AbstractBase06.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\AbstractBase07\AbstractBase07.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass01\NestedBaseClass01.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass02\NestedBaseClass02.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass03\NestedBaseClass03.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass04\NestedBaseClass04.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass05\NestedBaseClass05.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedBaseClass06\NestedBaseClass06.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedClass01\NestedClass01.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedClass03\NestedClass03.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface01\NestedInterface01.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface02\NestedInterface02.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface03\NestedInterface03.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface04\NestedInterface04.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface05\NestedInterface05.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface06\NestedInterface06.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface07\NestedInterface07.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedInterface08\NestedInterface08.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedStruct01\NestedStruct01.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Instantiation\Positive\NestedStruct03\NestedStruct03.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\General\Base01b_seq_ser\Base01b_seq_ser.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Variance\IL\Unbox005\Unbox005.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Variance\IL\Unbox006\Unbox006.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\CctorThrowInlinedStatic\CctorThrowInlinedStatic.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\CctorThrowLDFTNStaticMethod\CctorThrowLDFTNStaticMethod.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\CctorThrowMethodAccess\CctorThrowMethodAccess.cmd" />
<IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\CctorThrowStaticField\CctorThrowStaticField.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\General\Base01a_auto\Base01a_auto.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\General\Base01a_auto_ser\Base01a_auto_ser.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\General\Base01a_seq\Base01a_seq.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\General\struct01_auto\struct01_auto.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\Specific\Positive007\Positive007.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\Specific\Positive008\Positive008.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\Specific\Positive009\Positive009.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Layout\Specific\Positive010\Positive010.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Misc\ConstraintsAndInheritance\ConstraintsAndInheritance.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Misc\TestWithManyParams\TestWithManyParams.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Statics\Regressions\524571\StaticsProblem1\StaticsProblem1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Statics\Regressions\524571\StaticsProblem2\StaticsProblem2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Statics\Regressions\524571\StaticsProblem3\StaticsProblem3.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase0\TestCase0.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase0_Nested_I\TestCase0_Nested_I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase0_Nested_I_Nested_J\TestCase0_Nested_I_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase0_Nested_J\TestCase0_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase0_Nested_J_Nested_I\TestCase0_Nested_J_Nested_I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase1\TestCase1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase1_Nested_I\TestCase1_Nested_I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase1_Nested_I_Nested_J\TestCase1_Nested_I_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase1_Nested_J\TestCase1_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase2\TestCase2.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase2_Nested_I\TestCase2_Nested_I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase2_Nested_I_Nested_J\TestCase2_Nested_I_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase2_Nested_J\TestCase2_Nested_J.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\InterfaceFolding\TestCase2_Nested_J_Nested_I\TestCase2_Nested_J_Nested_I.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_568786\4_Misc\ConstrainedMethods\ConstrainedMethods.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_568786\4_Misc\RecursiveGen\RecursiveGen.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\Variance\Covariant_CollapsedInterfaces\HelloWorld\HelloWorld.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\Variance\Covariant_InherittedCollision\HelloWorld\HelloWorld.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\Variance\Variant_CollapsedInterfaces\HelloWorld\HelloWorld.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TSAmbiguities\Variance\Variant_InherittedCollision\HelloWorld\HelloWorld.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\v1\Beta1\Layout\Matrix\cs\L-1-2-1\L-1-2-1.cmd" />
- <IncludeList Include="$(XunitTestBinBase)\Regressions\expl_double\expl_double\expl_double.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\CctorThrowStaticFieldBFI\CctorThrowStaticFieldBFI.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\TypeLoadInitExcep\TypeLoadInitExcep.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\TypeLoadInitExcepBFI\TypeLoadInitExcepBFI.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CctorsWithSideEffects\UntrustedCodeBFI\UntrustedCodeBFI.cmd" />
+ <IncludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeInitialization\CircularCctors\CircularCctorFourThreadsBFI\CircularCctorFourThreadsBFI.cmd" />
</ItemGroup>
</Project>
diff --git a/tests/Top200.unix.txt b/tests/Top200.unix.txt
index a435e5db7..22f16baf9 100644
--- a/tests/Top200.unix.txt
+++ b/tests/Top200.unix.txt
@@ -205,7 +205,6 @@ Interop/PrimitiveMarshalling/EnumMarshalling/EnumTest/EnumTest.sh
Interop/FuncPtrAsDelegateParam/FuncPtrAsDelegateParam/FuncPtrAsDelegateParam.sh
Interop/MarshalAPI/FunctionPointer/FunctionPtrTest/FunctionPtrTest.sh
Interop/MarshalAPI/GetObjectsForNativeVariants/GetObjectsForNativeVariants/GetObjectsForNativeVariants.sh
-Interop/StringMarshalling/LPSTR/LPSTRTest/LPSTRTest.sh
Interop/ArrayMarshalling/BoolArray/MarshalBoolArrayTest/MarshalBoolArrayTest.sh
Interop/PrimitiveMarshalling/UIntPtr/PInvokeUIntPtrTest/PInvokeUIntPtrTest.sh
Interop/MarshalAPI/ReadWrite/ReadWriteByte/ReadWriteByte.sh
diff --git a/tests/runtest.sh b/tests/runtest.sh
index 0144371de..51104cf81 100755
--- a/tests/runtest.sh
+++ b/tests/runtest.sh
@@ -41,6 +41,9 @@ run_test_dir()
if [ "${__mode}" = "Cpp" ]; then
__extra_args="${__extra_args} /p:NativeCodeGen=cpp"
fi
+ if [ "${__mode}" = "Wasm" ]; then
+ __extra_args="${__extra_args} /p:NativeCodeGen=wasm"
+ fi
if [ -n "${__extra_cxxflags}" ]; then
__extra_cxxflags="/p:AdditionalCppCompilerFlags=\"${__extra_cxxflags}\""
fi
@@ -55,8 +58,8 @@ run_test_dir()
local __msbuild_dir=${CoreRT_TestRoot}/../Tools
- echo ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj
- ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:OSGroup=${CoreRT_BuildOS} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj
+ echo ${__msbuild_dir}/msbuild.sh /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} ${__extra_cxxflags} ${__extra_linkflags} ${__dir_path}/${__filename}.csproj
+ ${__msbuild_dir}/msbuild.sh /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:OSGroup=${CoreRT_BuildOS} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} ${__extra_cxxflags} ${__extra_linkflags} ${__dir_path}/${__filename}.csproj
local __exitcode=$?
@@ -176,6 +179,12 @@ while [ "$1" != "" ]; do
usage
exit 1
;;
+ wasm)
+ CoreRT_BuildArch=wasm
+ CoreRT_BuildOS=WebAssembly
+ CoreRT_TestCompileMode=wasm
+ CoreRT_TestRun=false
+ ;;
x86)
CoreRT_BuildArch=x86
;;
@@ -319,7 +328,8 @@ __CppTotalTests=0
__CppPassedTests=0
__JitTotalTests=0
__JitPassedTests=0
-
+__WasmTotalTests=0
+__WasmPassedTests=0
if [ ! -d ${__CoreRTTestBinDir} ]; then
mkdir -p ${__CoreRTTestBinDir}
@@ -327,23 +337,31 @@ fi
echo > ${__CoreRTTestBinDir}/testResults.tmp
__BuildOsLowcase=$(echo "${CoreRT_BuildOS}" | tr '[:upper:]' '[:lower:]')
-__TestSearchPath=src/Simple/${CoreRT_TestName}
+__TestSearchPath=${CoreRT_TestRoot}/src/Simple/${CoreRT_TestName}
for csproj in $(find ${__TestSearchPath} -name "*.csproj")
do
- if [ ! -e `dirname ${csproj}`/no_unix ]; then
- if [ "${CoreRT_TestCompileMode}" != "cpp" ]; then
+ if [ -e `dirname ${csproj}`/no_unix ]; then continue; fi
+ if [ -e `dirname ${csproj}`/no_linux ] && [ "${CoreRT_HostOS}" != "OSX" ]; then continue; fi
+
+ if [ "${CoreRT_TestCompileMode}" = "ryujit" ] || [ "${CoreRT_TestCompileMode}" = "" ]; then
+ if [ ! -e `dirname ${csproj}`/no_ryujit ]; then
run_test_dir ${csproj} "Jit"
fi
+ fi
+ if [ "${CoreRT_TestCompileMode}" = "cpp" ] || [ "${CoreRT_TestCompileMode}" = "" ]; then
if [ ! -e `dirname ${csproj}`/no_cpp ]; then
- if [ "${CoreRT_TestCompileMode}" != "ryujit" ]; then
- run_test_dir ${csproj} "Cpp" "$CoreRT_ExtraCXXFlags" "$CoreRT_ExtraLinkFlags"
- fi
+ run_test_dir ${csproj} "Cpp" "$CoreRT_ExtraCXXFlags" "$CoreRT_ExtraLinkFlags"
+ fi
+ fi
+ if [ "${CoreRT_TestCompileMode}" = "wasm" ]; then
+ if [ -e `dirname ${csproj}`/wasm ]; then
+ run_test_dir ${csproj} "Wasm"
fi
fi
done
-__TotalTests=$((${__JitTotalTests} + ${__CppTotalTests}))
-__PassedTests=$((${__JitPassedTests} + ${__CppPassedTests}))
+__TotalTests=$((${__JitTotalTests} + ${__CppTotalTests} + ${__WasmTotalTests}))
+__PassedTests=$((${__JitPassedTests} + ${__CppPassedTests} + ${__WasmPassedTests}))
__FailedTests=$((${__TotalTests} - ${__PassedTests}))
if [ "$CoreRT_MultiFileConfiguration" = "MultiModule" ]; then
@@ -366,19 +384,26 @@ echo "</assemblies>" >> ${__TestResultsLog}
echo "JIT - TOTAL: ${__JitTotalTests} PASSED: ${__JitPassedTests}"
echo "CPP - TOTAL: ${__CppTotalTests} PASSED: ${__CppPassedTests}"
+echo "WASM - TOTAL: ${__WasmTotalTests} PASSED: ${__WasmPassedTests}"
-if [ ${__JitTotalTests} == 0 ]; then
+if [ ${__JitTotalTests} == 0 ] && [ "${CoreRT_TestCompileMode}" != "wasm" ]; then
exit 1
fi
-
-if [ ${__CppTotalTests} == 0 ]; then
+if [ ${__CppTotalTests} == 0 ] && [ "${CoreRT_TestCompileMode}" != "wasm" ]; then
exit 1
fi
+if [ ${__WasmTotalTests} == 0 ] && [ "${CoreRT_TestCompileMode}" = "wasm" ]; then
+ exit 1
+fi
+
if [ ${__JitTotalTests} -gt ${__JitPassedTests} ]; then
exit 1
fi
if [ ${__CppTotalTests} -gt ${__CppPassedTests} ]; then
exit 1
fi
+if [ ${__WasmTotalTests} -gt ${__WasmPassedTests} ]; then
+ exit 1
+fi
exit 0
diff --git a/tests/src/Simple/HelloWasm/HelloWasm.csproj b/tests/src/Simple/HelloWasm/HelloWasm.csproj
index b767a5dbd..f070f287a 100644
--- a/tests/src/Simple/HelloWasm/HelloWasm.csproj
+++ b/tests/src/Simple/HelloWasm/HelloWasm.csproj
@@ -2,9 +2,13 @@
<ItemGroup>
<Compile Include="*.cs" />
- <ProjectReference Include="CpObj.ilproj" />
- <IlcArg Include="-r:$(IntermediateOutputPath)\CpObj.dll" />
+ <ProjectReference Include="CpObj.ilproj" Condition="'$(OS)' == 'Windows_NT'" />
+ <IlcArg Include="-r:$(IntermediateOutputPath)\CpObj.dll" Condition="'$(OS)' == 'Windows_NT'" />
</ItemGroup>
+ <PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
+ <DefineConstants>PLATFORM_WINDOWS;$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SimpleTest.targets))\SimpleTest.targets" />
</Project>
diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs
index 25886330c..75864c2e0 100644
--- a/tests/src/Simple/HelloWasm/Program.cs
+++ b/tests/src/Simple/HelloWasm/Program.cs
@@ -4,7 +4,9 @@
using System;
using System.Runtime.InteropServices;
+#if PLATFORM_WINDOWS
using CpObj;
+#endif
internal static class Program
{
@@ -62,6 +64,18 @@ internal static class Program
var boxedStruct = (object)new BoxStubTest { Value = "Boxed Stub Test: Ok." };
PrintLine(boxedStruct.ToString());
+ int subResult = tempInt - 1;
+ if (subResult == 8)
+ {
+ PrintLine("Subtraction Test: Ok.");
+ }
+
+ int divResult = tempInt / 3;
+ if (divResult == 3)
+ {
+ PrintLine("Division Test: Ok.");
+ }
+
var not = Not(0xFFFFFFFF) == 0x00000000;
if (not)
{
@@ -110,6 +124,7 @@ internal static class Program
PrintLine("SwitchOpDefault test: Ok.");
}
+#if PLATFORM_WINDOWS
var cpObjTestA = new TestValue { Field = 1234 };
var cpObjTestB = new TestValue { Field = 5678 };
CpObjTest.CpObj(ref cpObjTestB, ref cpObjTestA);
@@ -117,6 +132,98 @@ internal static class Program
{
PrintLine("CpObj test: Ok.");
}
+#endif
+
+ Func<int> staticDelegate = StaticDelegateTarget;
+ if(staticDelegate() == 7)
+ {
+ PrintLine("Static delegate test: Ok.");
+ }
+
+ tempObj.TestInt = 8;
+ Func<int> instanceDelegate = tempObj.InstanceDelegateTarget;
+ if(instanceDelegate() == 8)
+ {
+ PrintLine("Instance delegate test: Ok.");
+ }
+
+ Action virtualDelegate = tempObj.VirtualDelegateTarget;
+ virtualDelegate();
+
+ var arrayTest = new BoxStubTest[] { new BoxStubTest { Value = "Hello" }, new BoxStubTest { Value = "Array" }, new BoxStubTest { Value = "Test" } };
+ foreach(var element in arrayTest)
+ PrintLine(element.Value);
+
+ arrayTest[1].Value = "Array load/store test: Ok.";
+ PrintLine(arrayTest[1].Value);
+
+ var largeArrayTest = new long[] { Int64.MaxValue, 0, Int64.MinValue, 0 };
+ if(largeArrayTest[0] == Int64.MaxValue &&
+ largeArrayTest[1] == 0 &&
+ largeArrayTest[2] == Int64.MinValue &&
+ largeArrayTest[3] == 0)
+ {
+ PrintLine("Large array load/store test: Ok.");
+ }
+
+ var smallArrayTest = new long[] { Int16.MaxValue, 0, Int16.MinValue, 0 };
+ if(smallArrayTest[0] == Int16.MaxValue &&
+ smallArrayTest[1] == 0 &&
+ smallArrayTest[2] == Int16.MinValue &&
+ smallArrayTest[3] == 0)
+ {
+ PrintLine("Small array load/store test: Ok.");
+ }
+
+ IntPtr returnedIntPtr = NewobjValueType();
+ if (returnedIntPtr.ToInt32() == 3)
+ {
+ PrintLine("Newobj value type test: Ok.");
+ }
+
+ StackallocTest();
+
+ IntToStringTest();
+
+ CastingTestClass castingTest = new DerivedCastingTestClass1();
+ if (((DerivedCastingTestClass1)castingTest).GetValue() == 1 && !(castingTest is DerivedCastingTestClass2))
+ {
+ PrintLine("Type casting with isinst & castclass to class test: Ok.");
+ }
+
+ // Instead of checking the result of `GetValue`, we use null check by now until interface dispatch is implemented.
+ if ((ICastingTest1)castingTest != null && !(castingTest is ICastingTest2))
+ {
+ PrintLine("Type casting with isinst & castclass to interface test: Ok.");
+ }
+
+ object arrayCastingTest = new BoxStubTest[] { new BoxStubTest { Value = "Array" }, new BoxStubTest { Value = "Cast" }, new BoxStubTest { Value = "Test" } };
+ PrintLine(((BoxStubTest[])arrayCastingTest)[0].Value);
+ PrintLine(((BoxStubTest[])arrayCastingTest)[1].Value);
+ PrintLine(((BoxStubTest[])arrayCastingTest)[2].Value);
+ if (!(arrayCastingTest is CastingTestClass[]))
+ {
+ PrintLine("Type casting with isinst & castclass to array test: Ok.");
+ }
+
+ ldindTest();
+
+ System.Diagnostics.Debugger.Break();
+
+ var testRuntimeHelpersInitArray = new long[] {1, 2, 3};
+ if(testRuntimeHelpersInitArray[0] == 1 &&
+ testRuntimeHelpersInitArray[1] == 2 &&
+ testRuntimeHelpersInitArray[2] == 3)
+ {
+ PrintLine("Runtime.Helpers array initialization test: Ok.");
+ }
+
+ PrintLine("Done");
+ }
+
+ private static int StaticDelegateTarget()
+ {
+ return 7;
}
private static unsafe void PrintString(string s)
@@ -186,6 +293,59 @@ internal static class Program
}
}
+ private static IntPtr NewobjValueType()
+ {
+ return new IntPtr(3);
+ }
+
+ private unsafe static void StackallocTest()
+ {
+ int* intSpan = stackalloc int[2];
+ intSpan[0] = 3;
+ intSpan[1] = 7;
+
+ if (intSpan[0] == 3 && intSpan[1] == 7)
+ {
+ PrintLine("Stackalloc test: Ok.");
+ }
+ }
+
+ private static void IntToStringTest()
+ {
+ PrintLine("Int to String Test: Ok if next line says 42.");
+ string intString = 42.ToString();
+ PrintLine(intString);
+ }
+
+ private unsafe static void ldindTest()
+ {
+ var ldindTarget = new TwoByteStr { first = byte.MaxValue, second = byte.MinValue };
+ var ldindField = &ldindTarget.first;
+ if((*ldindField) == byte.MaxValue)
+ {
+ ldindTarget.second = byte.MaxValue;
+ *ldindField = byte.MinValue;
+ //ensure there isnt any overwrite of nearby fields
+ if(ldindTarget.first == byte.MinValue && ldindTarget.second == byte.MaxValue)
+ {
+ PrintLine("ldind test: Ok.");
+ }
+ else if(ldindTarget.first != byte.MinValue)
+ {
+ PrintLine("ldind test: Failed didnt update target.");
+ }
+ else
+ {
+ PrintLine("ldind test: Failed overwrote data");
+ }
+ }
+ else
+ {
+ uint ldindFieldValue = *ldindField;
+ PrintLine("ldind test: Failed." + ldindFieldValue.ToString());
+ }
+ }
+
[DllImport("*")]
private static unsafe extern int printf(byte* str, byte* unused);
}
@@ -203,11 +363,19 @@ public struct BoxStubTest
{
return Value;
}
+
+ public string GetValue()
+ {
+ Program.PrintLine("BoxStubTest.GetValue called");
+ Program.PrintLine(Value);
+ return Value;
+ }
}
public class TestClass
{
- public string TestString {get; set;}
+ public string TestString { get; set; }
+ public int TestInt { get; set; }
public TestClass(int number)
{
@@ -230,6 +398,16 @@ public class TestClass
{
Program.PrintLine("Virtual Slot Test 2: Ok");
}
+
+ public int InstanceDelegateTarget()
+ {
+ return TestInt;
+ }
+
+ public virtual void VirtualDelegateTarget()
+ {
+ Program.PrintLine("Virtual delegate incorrectly dispatched to base.");
+ }
}
public class TestDerivedClass : TestClass
@@ -248,5 +426,34 @@ public class TestDerivedClass : TestClass
{
throw new Exception();
}
+
+ public override void VirtualDelegateTarget()
+ {
+ Program.PrintLine("Virtual Delegate Test: Ok");
+ }
+}
+
+public interface ICastingTest1
+{
+ int GetValue();
}
+public interface ICastingTest2
+{
+ int GetValue();
+}
+
+public abstract class CastingTestClass
+{
+ public abstract int GetValue();
+}
+
+public class DerivedCastingTestClass1 : CastingTestClass, ICastingTest1
+{
+ public override int GetValue() => 1;
+}
+
+public class DerivedCastingTestClass2 : CastingTestClass, ICastingTest2
+{
+ public override int GetValue() => 2;
+}
diff --git a/tests/src/Simple/HelloWasm/no_unix b/tests/src/Simple/HelloWasm/no_unix
deleted file mode 100644
index e6c22996c..000000000
--- a/tests/src/Simple/HelloWasm/no_unix
+++ /dev/null
@@ -1 +0,0 @@
-Doesn't work on OSX. \ No newline at end of file
diff --git a/tests/src/Simple/PInvoke/PInvoke.csproj b/tests/src/Simple/PInvoke/PInvoke.csproj
index 05f571f84..d51b76676 100644
--- a/tests/src/Simple/PInvoke/PInvoke.csproj
+++ b/tests/src/Simple/PInvoke/PInvoke.csproj
@@ -19,7 +19,7 @@
</PropertyGroup>
<ItemGroup>
- <AdditionalNativeLibrary Include="$(PInvokeNativeObject)" />
+ <NativeLibrary Include="$(PInvokeNativeObject)" />
</ItemGroup>
diff --git a/tests/src/Simple/Reflection/Reflection.cs b/tests/src/Simple/Reflection/Reflection.cs
index 4454e9415..322650a2c 100644
--- a/tests/src/Simple/Reflection/Reflection.cs
+++ b/tests/src/Simple/Reflection/Reflection.cs
@@ -423,6 +423,21 @@ internal class ReflectionTest
}
}
+ class Gen<T> { }
+
+ interface IFoo<out T>
+ {
+ string Frob();
+ }
+
+ class Foo<T> : IFoo<Gen<T>>
+ {
+ public string Frob()
+ {
+ return typeof(T).ToString();
+ }
+ }
+
public static void Run()
{
Console.WriteLine(nameof(TestInterfaceMethod));
@@ -431,11 +446,16 @@ internal class ReflectionTest
if (string.Empty.Length > 0)
{
((IFoo)new Foo()).Frob(1);
+ ((IFoo<object>)new Foo<string>()).Frob();
}
object result = InvokeTestMethod(typeof(IFoo), "Frob", new Foo(), 42);
if ((string)result != "42")
throw new Exception();
+
+ result = InvokeTestMethod(typeof(IFoo<object>), "Frob", new Foo<string>());
+ if ((string)result != "System.String")
+ throw new Exception();
}
}
diff --git a/tests/testenv.sh b/tests/testenv.sh
index 69a636bca..f2b78e113 100755
--- a/tests/testenv.sh
+++ b/tests/testenv.sh
@@ -14,6 +14,9 @@ for i in "$@"
usage
exit 1
;;
+ wasm)
+ CoreRT_BuildArch=wasm
+ ;;
x86)
CoreRT_BuildArch=x86
;;
@@ -47,7 +50,7 @@ for i in "$@"
done
if [ -z ${CoreRT_BuildArch} ]; then
- echo "Set CoreRT_BuildArch to x86/x64/arm/arm64"
+ echo "Set CoreRT_BuildArch to x86/x64/arm/arm64/wasm"
exit -1
fi
@@ -56,31 +59,39 @@ if [ -z ${CoreRT_BuildType} ]; then
exit -1
fi
+
# Use uname to determine what the OS is.
-OSName=$(uname -s)
+export OSName=$(uname -s)
case $OSName in
- Darwin)
- CoreRT_BuildOS=OSX
- ;;
-
- FreeBSD)
- CoreRT_BuildOS=FreeBSD
- ;;
-
- Linux)
- CoreRT_BuildOS=Linux
- ;;
-
- NetBSD)
- CoreRT_BuildOS=NetBSD
- ;;
-
- *)
- echo "Unsupported OS $OSName detected, configuring as if for Linux"
- CoreRT_BuildOS=Linux
- ;;
+ Darwin)
+ export CoreRT_HostOS=OSX
+ ;;
+
+ FreeBSD)
+ export CoreRT_HostOS=FreeBSD
+ ;;
+
+ Linux)
+ export CoreRT_HostOS=Linux
+ ;;
+
+ NetBSD)
+ export CoreRT_HostOS=NetBSD
+ ;;
+
+ *)
+ echo "Unsupported OS $OSName detected, configuring as if for Linux"
+ export CoreRT_HostOS=Linux
+ ;;
esac
+export CoreRT_BuildOS=${CoreRT_HostOS}
+
+# Overwrite __BuildOS with WebAssembly if wasm is target build arch, but keep the CoreRT_HostOS to match the Host OS
+if [ "$__BuildArch" == "wasm" ]; then
+ export CoreRT_BuildOS=WebAssembly
+fi
+
export CoreRT_BuildArch
export CoreRT_BuildType
export CoreRT_BuildOS
@@ -88,3 +99,13 @@ export CoreRT_BuildOS
__ScriptDir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
export CoreRT_ToolchainDir=${__ScriptDir}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}
+
+# CI_SPECIFIC - On CI machines, $HOME may not be set. In such a case, create a subfolder and set the variable to set.
+# This is needed by CLI to function.
+if [ -z "$HOME" ]; then
+ if [ ! -d "$__ScriptDir/../temp_home" ]; then
+ mkdir "$__ScriptDir/../temp_home"
+ fi
+ export HOME=$__ScriptDir/../temp_home
+ echo "HOME not defined; setting it to $HOME"
+fi