summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Sullivan <briansul@microsoft.com>2019-03-19 14:56:54 -0700
committerBrian Sullivan <briansul@microsoft.com>2019-03-19 14:56:54 -0700
commitb2c397b649181f793d6c29f1de78d0aee0a52596 (patch)
tree0e3cecc368c8fa5b2560c795ee793b72d4a9404a
parented9a4bf1392e247a12c119a551cd892c9d9e8d1f (diff)
parentaccc6eb0f0f669494bee0788884b5a10101f1243 (diff)
downloadcoreclr-b2c397b649181f793d6c29f1de78d0aee0a52596.tar.gz
coreclr-b2c397b649181f793d6c29f1de78d0aee0a52596.tar.bz2
coreclr-b2c397b649181f793d6c29f1de78d0aee0a52596.zip
Merge branch 'master' into hva-tests
-rw-r--r--Documentation/Profiling/Profiler Breaking Changes.md6
-rw-r--r--Documentation/Profiling/davbr-blog-archive/Creating an IL-rewriting profiler.md2
-rw-r--r--Documentation/building/cross-building.md2
-rw-r--r--Documentation/building/debugging-instructions.md2
-rw-r--r--Documentation/building/windows-instructions.md2
-rw-r--r--Documentation/design-docs/assemblyloadcontext.md4
-rw-r--r--Documentation/design-docs/code-versioning-profiler-breaking-changes.md2
-rw-r--r--Documentation/design-docs/code-versioning.md4
-rw-r--r--Documentation/design-docs/inlining-plans.md6
-rw-r--r--Documentation/design-docs/tiered-compilation.md4
-rw-r--r--Documentation/project-docs/jit-testing.md6
-rwxr-xr-xbuild-test.sh41
-rwxr-xr-xbuild.sh21
-rw-r--r--clr.defines.targets1
-rw-r--r--clrdefinitions.cmake1
-rw-r--r--configurecompiler.cmake1
-rw-r--r--eng/Version.Details.xml24
-rw-r--r--eng/Versions.props10
-rw-r--r--eng/common/PublishToPackageFeed.proj24
-rw-r--r--eng/common/generate-graph-files.ps12
-rw-r--r--eng/common/init-tools-native.ps114
-rw-r--r--eng/common/templates/job/generate-graph-files.yml48
-rw-r--r--eng/common/templates/job/publish-build-assets.yml1
-rw-r--r--eng/common/templates/jobs/jobs.yml19
-rw-r--r--eng/common/templates/steps/send-to-helix.yml2
-rw-r--r--eng/platform-matrix.yml6
-rw-r--r--global.json4
-rwxr-xr-xnetci.groovy46
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj14
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems4
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs6
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs1
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Enum.cs (renamed from src/System.Private.CoreLib/src/System/Enum.cs)117
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Memory.cs32
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs866
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.cs311
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs32
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/Reflection/CorElementType.cs46
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs7
-rw-r--r--src/System.Private.CoreLib/shared/System/Span.Fast.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/String.cs13
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Rune.cs283
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs8
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs106
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/ThrowHelper.cs2
-rw-r--r--src/System.Private.CoreLib/src/System/Char8.cs69
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs12
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs97
-rw-r--r--src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs113
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs8
-rw-r--r--src/System.Private.CoreLib/src/System/Reflection/MdImport.cs40
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs13
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8Extensions.cs367
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Construction.cs223
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs109
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Searching.cs93
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.cs252
-rw-r--r--src/ToolBox/SOS/Strike/apollososdocs.txt4
-rw-r--r--src/ToolBox/SOS/Strike/sosdocs.txt4
-rw-r--r--src/classlibnative/bcltype/objectnative.cpp3
-rw-r--r--src/debug/daccess/daccess.cpp4
-rw-r--r--src/debug/debug-pal/CMakeLists.txt2
-rw-r--r--src/debug/debug-pal/unix/diagnosticsipc.cpp142
-rw-r--r--src/debug/debug-pal/win/diagnosticsipc.cpp142
-rw-r--r--src/debug/ee/debugger.cpp4
-rw-r--r--src/debug/inc/diagnosticsipc.h68
-rw-r--r--src/gc/sample/gcenv.ee.cpp4
-rw-r--r--src/gc/unix/cgroup.cpp13
-rw-r--r--src/gc/unix/gcenv.unix.cpp2
-rw-r--r--src/gc/windows/gcenv.windows.cpp21
-rw-r--r--src/inc/dacvars.h3
-rw-r--r--src/inc/mscorsvc.idl2
-rw-r--r--src/inc/registrywrapper.h2
-rw-r--r--src/inc/stresslog.h2
-rw-r--r--src/inc/utilcode.h11
-rw-r--r--src/jit/CMakeLists.txt5
-rw-r--r--src/jit/bitset.h2
-rw-r--r--src/jit/bitsetasshortlong.h5
-rw-r--r--src/jit/codegen.h2
-rw-r--r--src/jit/codegenarm64.cpp14
-rw-r--r--src/jit/codegencommon.cpp10
-rw-r--r--src/jit/codegeninterface.h19
-rw-r--r--src/jit/codegenxarch.cpp8
-rw-r--r--src/jit/compiler.cpp4
-rw-r--r--src/jit/compiler.h12
-rw-r--r--src/jit/compiler.hpp2
-rw-r--r--src/jit/earlyprop.cpp10
-rw-r--r--src/jit/ee_il_dll.cpp9
-rw-r--r--src/jit/emit.h2
-rw-r--r--src/jit/flowgraph.cpp22
-rw-r--r--src/jit/gcencode.cpp2
-rw-r--r--src/jit/gtlist.h138
-rw-r--r--src/jit/hwintrinsicArm64.cpp6
-rw-r--r--src/jit/hwintrinsiclistArm64.h42
-rw-r--r--src/jit/importer.cpp34
-rw-r--r--src/jit/jitconfigvalues.h29
-rw-r--r--src/jit/jitstd/hashtable.h1
-rw-r--r--src/jit/lclvars.cpp4
-rw-r--r--src/jit/loopcloning.h2
-rw-r--r--src/jit/morph.cpp88
-rw-r--r--src/jit/regset.h2
-rw-r--r--src/jit/scopeinfo.cpp77
-rw-r--r--src/jit/unwindarm.cpp2
-rw-r--r--src/jit/utils.cpp6
-rw-r--r--src/nativeresources/rctocpp.awk4
-rw-r--r--src/pal/automation/automation.py78
-rw-r--r--src/pal/automation/compile.py70
-rw-r--r--src/pal/automation/tests.py33
-rw-r--r--src/pal/automation/util.py97
-rw-r--r--src/pal/inc/pal.h11
-rw-r--r--src/pal/src/misc/cgroup.cpp18
-rw-r--r--src/pal/src/numa/numa.cpp2
-rw-r--r--src/pal/src/thread/process.cpp54
-rwxr-xr-xsrc/pal/tools/gen-buildsys-clang.sh13
-rwxr-xr-xsrc/pal/tools/gen-buildsys-gcc.sh11
-rw-r--r--src/strongname/api/common.h3
-rw-r--r--src/tools/metainfo/metainfo.cpp2
-rw-r--r--src/utilcode/registrywrapper.cpp4
-rw-r--r--src/utilcode/util.cpp354
-rw-r--r--src/vm/CMakeLists.txt9
-rw-r--r--src/vm/appdomain.cpp6
-rw-r--r--src/vm/ceeload.cpp66
-rw-r--r--src/vm/ceeload.h9
-rw-r--r--src/vm/ceemain.cpp3
-rw-r--r--src/vm/classnames.h4
-rw-r--r--src/vm/clsload.cpp26
-rw-r--r--src/vm/codeversion.cpp2
-rw-r--r--src/vm/comdelegate.cpp78
-rw-r--r--src/vm/common.h4
-rw-r--r--src/vm/compile.cpp2
-rw-r--r--src/vm/crossgencompile.cpp6
-rw-r--r--src/vm/diagnosticserver.cpp171
-rw-r--r--src/vm/diagnosticserver.h58
-rw-r--r--src/vm/diagnosticsprotocol.h54
-rw-r--r--src/vm/ecall.cpp70
-rw-r--r--src/vm/ecall.h18
-rw-r--r--src/vm/ecalllist.h20
-rw-r--r--src/vm/eepolicy.cpp28
-rw-r--r--src/vm/eepolicy.h2
-rw-r--r--src/vm/encee.cpp1
-rw-r--r--src/vm/eventpipe.cpp428
-rw-r--r--src/vm/eventpipe.h326
-rw-r--r--src/vm/eventpipeconfiguration.cpp120
-rw-r--r--src/vm/eventpipeconfiguration.h25
-rw-r--r--src/vm/eventpipeeventinstance.h2
-rw-r--r--src/vm/eventpipeeventsource.cpp1
-rw-r--r--src/vm/eventpipefile.cpp9
-rw-r--r--src/vm/eventpipeinternal.cpp270
-rw-r--r--src/vm/eventpipeinternal.h107
-rw-r--r--src/vm/eventpipeprotocolhelper.cpp156
-rw-r--r--src/vm/eventpipeprotocolhelper.h35
-rw-r--r--src/vm/eventpipeprovider.h2
-rw-r--r--src/vm/eventpipesession.cpp226
-rw-r--r--src/vm/eventpipesession.h74
-rw-r--r--src/vm/eventpipesessionprovider.cpp195
-rw-r--r--src/vm/eventpipesessionprovider.h81
-rw-r--r--src/vm/excep.cpp2
-rw-r--r--src/vm/exceptionhandling.cpp1
-rw-r--r--src/vm/fastserializer.cpp125
-rw-r--r--src/vm/fastserializer.h71
-rw-r--r--src/vm/gcenv.os.cpp21
-rw-r--r--src/vm/gchandleutilities.cpp2
-rw-r--r--src/vm/gchandleutilities.h2
-rw-r--r--src/vm/gchelpers.cpp77
-rw-r--r--src/vm/gchelpers.h17
-rw-r--r--src/vm/gdbjit.cpp4
-rw-r--r--src/vm/interpreter.cpp2
-rw-r--r--src/vm/jithelpers.cpp71
-rw-r--r--src/vm/jitinterface.cpp50
-rw-r--r--src/vm/jitinterface.h5
-rw-r--r--src/vm/jitinterfacegen.cpp9
-rw-r--r--src/vm/marshalnative.cpp10
-rw-r--r--src/vm/metasig.h14
-rw-r--r--src/vm/methodtable.h2
-rw-r--r--src/vm/methodtablebuilder.cpp13
-rw-r--r--src/vm/mscorlib.cpp2
-rw-r--r--src/vm/mscorlib.h18
-rw-r--r--src/vm/object.h64
-rw-r--r--src/vm/object.inl16
-rw-r--r--src/vm/proftoeeinterfaceimpl.cpp4
-rw-r--r--src/vm/reflectioninvocation.cpp18
-rw-r--r--src/vm/runtimecallablewrapper.cpp21
-rw-r--r--src/vm/synch.cpp12
-rw-r--r--src/vm/threadpoolrequest.h4
-rw-r--r--src/vm/vars.cpp3
-rw-r--r--src/vm/vars.hpp14
-rw-r--r--src/zap/zapimport.cpp8
-rw-r--r--tests/CoreFX/CoreFX.issues.json4
-rw-r--r--tests/arm64/corefx_test_exclusions.txt11
-rw-r--r--tests/dir.props2
-rw-r--r--tests/scripts/format.py10
-rwxr-xr-xtests/scripts/run-pmi-diffs.py10
-rw-r--r--tests/src/Interop/SizeConst/SizeConstTest.cs2
-rw-r--r--tests/src/JIT/Methodical/cctor/misc/testlib.cs2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/BenchmarksGame/regex-redux/regex-redux-5.cs2
-rw-r--r--tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericClasses.csproj2
-rw-r--r--tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericStructs.csproj2
-rw-r--r--tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericTypesMix.csproj2
-rw-r--r--tests/src/Regressions/coreclr/22728/createdelegate.il103
-rw-r--r--tests/src/Regressions/coreclr/22728/createdelegate.ilproj35
-rw-r--r--tests/src/Regressions/coreclr/22728/debug15.il133
-rw-r--r--tests/src/Regressions/coreclr/22728/debug15.ilproj35
-rw-r--r--tests/src/performance/Scenario/JitBench/unofficial_dotnet/README.md2
-rw-r--r--tests/src/performance/linkbench/scripts/clone.cmd2
213 files changed, 6260 insertions, 2822 deletions
diff --git a/Documentation/Profiling/Profiler Breaking Changes.md b/Documentation/Profiling/Profiler Breaking Changes.md
new file mode 100644
index 0000000000..f63b22764e
--- /dev/null
+++ b/Documentation/Profiling/Profiler Breaking Changes.md
@@ -0,0 +1,6 @@
+# Profiler Breaking Changes #
+
+Over time we will need to modify the Profiler APIs, this document will serve as a record of any breaking changes.
+
+1. Code Versioning introduced changes documented [here](../design-docs/code-versioning-profiler-breaking-changes.md)
+2. The work to allow adding new types and methods after module load means ICorProfilerInfo7::ApplyMetadata will now potentially trigger a GC, and will not be callable in situations where a GC can not happen (for example ICorProfilerCallback::RootReferences). \ No newline at end of file
diff --git a/Documentation/Profiling/davbr-blog-archive/Creating an IL-rewriting profiler.md b/Documentation/Profiling/davbr-blog-archive/Creating an IL-rewriting profiler.md
index 21678cad4e..7af4739d8b 100644
--- a/Documentation/Profiling/davbr-blog-archive/Creating an IL-rewriting profiler.md
+++ b/Documentation/Profiling/davbr-blog-archive/Creating an IL-rewriting profiler.md
@@ -45,7 +45,7 @@ And then there's the worst of both worlds: when you need to rewrite IL to call i
**Q: Has anyone else tried making an IL-rewriting profiler?**
-Sure. If you want to learn from other people's experiences, read through the [Building Development and Diagnostic Tools for .Net Forum](http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=868&SiteID=1). Here are some interesting threads:
+Sure. If you want to learn from other people's experiences, read through the [Building Development and Diagnostic Tools for .NET Forum](http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=868&SiteID=1). Here are some interesting threads:
[http://social.msdn.microsoft.com/Forums/en-NZ/netfxtoolsdev/thread/5f30596b-e7b7-4b1f-b8e1-8172aa8dde31](http://social.msdn.microsoft.com/Forums/en-NZ/netfxtoolsdev/thread/5f30596b-e7b7-4b1f-b8e1-8172aa8dde31)
[http://social.msdn.microsoft.com/Forums/en-GB/netfxtoolsdev/thread/c352266f-ded3-4ee2-b2f9-fbeb41a70c27](http://social.msdn.microsoft.com/Forums/en-GB/netfxtoolsdev/thread/c352266f-ded3-4ee2-b2f9-fbeb41a70c27)
diff --git a/Documentation/building/cross-building.md b/Documentation/building/cross-building.md
index 8554f731a5..9baca18377 100644
--- a/Documentation/building/cross-building.md
+++ b/Documentation/building/cross-building.md
@@ -139,7 +139,7 @@ The output is at bin/Product/<BuildOS>.arm.Debug/System.Private.CoreLib.dll.
```
lgs@ubuntu ~/git/coreclr/ $ file ./bin/Product/Linux.arm.Debug/System.Private.CoreLib.dll
./bin/Product/Linux.arm.Debug/System.Private.CoreLib.dll: PE32 executable (DLL)
- (console) ARMv7 Thumb Mono/.Net assembly, for MS Windows
+ (console) ARMv7 Thumb Mono/.NET assembly, for MS Windows
```
Building coreclr for Linux ARM Emulator
diff --git a/Documentation/building/debugging-instructions.md b/Documentation/building/debugging-instructions.md
index a97433300a..86468b6bbf 100644
--- a/Documentation/building/debugging-instructions.md
+++ b/Documentation/building/debugging-instructions.md
@@ -208,4 +208,4 @@ Using Visual Studio
- For managed debugging, there are some settings in Debug->Options, Debugging->General that might be useful:
- Uncheck 'Just My Code'. This will allow you debug into the framework libraries.
- Check 'Enable .NET Framework Source Stepping.' This will configure the debugger to download symbols and source automatically for runtime framework binaries. If you built the framework yourself this may be irrelevant because you already have all the source on your machine but it doesn't hurt.
- - Check 'Suppress JIT optimzation on module load'. This tells the debugger to tell the .NET runtime JIT to generate debuggable code even for modules that may not have been compiled in a 'Debug' configuration by the C# compiler. This code is slower, but it provides much higher fidelity breakpoints, stepping, and local variable access. It is the same difference you see when debugging .Net apps in the 'Debug' project configuration vs. the 'Release' project configuration.
+ - Check 'Suppress JIT optimzation on module load'. This tells the debugger to tell the .NET runtime JIT to generate debuggable code even for modules that may not have been compiled in a 'Debug' configuration by the C# compiler. This code is slower, but it provides much higher fidelity breakpoints, stepping, and local variable access. It is the same difference you see when debugging .NET apps in the 'Debug' project configuration vs. the 'Release' project configuration.
diff --git a/Documentation/building/windows-instructions.md b/Documentation/building/windows-instructions.md
index b526148895..afb216fccb 100644
--- a/Documentation/building/windows-instructions.md
+++ b/Documentation/building/windows-instructions.md
@@ -88,7 +88,7 @@ Powershell version must be 3.0 or higher. This should be the case for Windows 8
## DotNet Core SDK
While not strictly needed to build or test the .NET Core repository, having the .NET Core SDK installed lets you use the dotnet.exe command to run .NET Core applications in the 'normal' way. We use this in the
[Using Your Build](../workflow/UsingYourBuild.md) instructions. Visual Studio should have
-installed the .NET Core SDK, but in case it did not you can get it from the [Installing the .Net Core SDK](https://www.microsoft.com/net/core) page.
+installed the .NET Core SDK, but in case it did not you can get it from the [Installing the .NET Core SDK](https://www.microsoft.com/net/core) page.
## Adding to the default PATH variable
diff --git a/Documentation/design-docs/assemblyloadcontext.md b/Documentation/design-docs/assemblyloadcontext.md
index cf6c92cda1..6f601f79cb 100644
--- a/Documentation/design-docs/assemblyloadcontext.md
+++ b/Documentation/design-docs/assemblyloadcontext.md
@@ -18,7 +18,7 @@ Every .NET Core app has a **LoadContext** instance created during .NET Core Runt
### Custom LoadContext
For scenarios that wish to have isolation between loaded assemblies, applications can create their own **LoadContext** instance by deriving from **System.Runtime.Loader.AssemblyLoadContext** type and loading the assemblies within that instance.
-Multiple assemblies with the same simple name cannot be loaded into a single load context (*Default* or *Custom*). Also, .Net Core ignores strong name token for assembly binding process.
+Multiple assemblies with the same simple name cannot be loaded into a single load context (*Default* or *Custom*). Also, .NET Core ignores strong name token for assembly binding process.
## How Load is attempted
@@ -64,7 +64,7 @@ This property will return a reference to the *Default LoadContext*.
### Load
-This method should be overriden in a *Custom LoadContext* if the intent is to override the assembly resolution that would be done during fallback to *Defaut LoadContext*
+This method should be overriden in a *Custom LoadContext* if the intent is to override the assembly resolution that would be done during fallback to *Default LoadContext*
### LoadFromAssemblyName
diff --git a/Documentation/design-docs/code-versioning-profiler-breaking-changes.md b/Documentation/design-docs/code-versioning-profiler-breaking-changes.md
index 538693101d..1889e1c4c9 100644
--- a/Documentation/design-docs/code-versioning-profiler-breaking-changes.md
+++ b/Documentation/design-docs/code-versioning-profiler-breaking-changes.md
@@ -1,6 +1,6 @@
# Code Versioning Profiler Breaking Changes #
-The runtime changes done as part of the code versioning feature will cause some (hopefully minor) breaking changes to be visible via the profiler API. My goal is to advertise these coming changes and solicit feedback about what will be easiest for profiler writers to absorb. Currently this feature is only under development in the .Net Core version of the runtime. If you solely support a profiler on full .Net Framework this change doesn't affect you.
+The runtime changes done as part of the code versioning feature will cause some (hopefully minor) breaking changes to be visible via the profiler API. My goal is to advertise these coming changes and solicit feedback about what will be easiest for profiler writers to absorb. Currently this feature is only under development in the .NET Core version of the runtime. If you solely support a profiler on full .NET Framework this change doesn't affect you.
## Underlying issue ##
diff --git a/Documentation/design-docs/code-versioning.md b/Documentation/design-docs/code-versioning.md
index 928f4a5512..4a4594bc5c 100644
--- a/Documentation/design-docs/code-versioning.md
+++ b/Documentation/design-docs/code-versioning.md
@@ -308,7 +308,7 @@ Generics can cause methods in domain-neutral modules to refer to types in domain
CoreCLR is largely moving away from multiple AppDomains but I left the domain distinctions in place for a few reasons:
1. Pragmatically the feature is a partial refactoring of ReJitManager and this is what ReJitManager did. Diverging from ReJitManager's design incurs extra time and risk of new issues.
-2. Maybe someday we will want to migrate similar functionality back to the desktop .Net SKU. Desktop supports multiple AppDomains so per-AppDomain handling would be required in that context.
+2. Maybe someday we will want to migrate similar functionality back to the desktop .NET SKU. Desktop supports multiple AppDomains so per-AppDomain handling would be required in that context.
AppDomain unloading however has not been handled. If CoreCLR supports proper AppDomain unload at some point or the code moves back to desktop runtime we will need to handle this gap.
@@ -355,7 +355,7 @@ All other information such as Edit and Continue IL modifications, or a profiler
- **Explicit** - These are configuration options that are encoded directly into the version tree data structures so they can be trivially read back at any time. Profiler ReJIT, generic instantiation, and tiered compilation settings are all explicit.
- **Ambient** - These are configuration values that are immutable once set and apply to all code versions of the same method. This can be treated the same as if the build stage gave an explicit configuration result for each generated code version, but the policy in that build stage always returns the same answer. Then as a memory footprint optimization rather than save N copies of the same answer in N different versions, the runtime stores the answer once and knows that it applies to all code versions. For example the debugger can set a debuggable codegen flag that causes all methods in a module to jit differently. The runtime stores that flag in the module for efficiency, but we can map it back to the code version concept as if every code version had that flag in it and it happens to be the same for all versions.
-- **Unrecorded** - The final case are configuration options that do change per code version, but they aren't recorded in the NativeCodeVersion/ILCodeVersion data structures. Logically this configuration is part of the code version regardless of the runtime's ability trivially access it/compute it on demand. For example a .Net profiler can control whether a particular jitting of a method has Profiler Enter/Leave/Tailcall probes in it based on its response to the FunctionIDMapper2 callback during JIT code generation. If there was already a version of this function generated without ELT and the profiler wanted a new version that does have ELT then the profiler needs to create a new code version. The only technique it has available to create new code versions is to use ReJIT, but ELT isn't part of explicit configuration that ReJIT collects for the new code version. Instead the profiler records in its own data structures that the new ReJIT version should have a certain ELT configuration setting. Whenever code jits that corresponds to this version the runtime will query the profiler and then the profiler will respond with the appropriate ELT setting. In this way the ELT has logically become part of the profiler ReJIT stage configuration even though the runtime has no explicit record of it. Generalizing from this example, the runtime expects that all unrecorded code version configuration can be treated as a hidden portion of the configuration that is supplied by one of the explicitly configured build stages.
+- **Unrecorded** - The final case are configuration options that do change per code version, but they aren't recorded in the NativeCodeVersion/ILCodeVersion data structures. Logically this configuration is part of the code version regardless of the runtime's ability trivially access it/compute it on demand. For example a .NET profiler can control whether a particular jitting of a method has Profiler Enter/Leave/Tailcall probes in it based on its response to the FunctionIDMapper2 callback during JIT code generation. If there was already a version of this function generated without ELT and the profiler wanted a new version that does have ELT then the profiler needs to create a new code version. The only technique it has available to create new code versions is to use ReJIT, but ELT isn't part of explicit configuration that ReJIT collects for the new code version. Instead the profiler records in its own data structures that the new ReJIT version should have a certain ELT configuration setting. Whenever code jits that corresponds to this version the runtime will query the profiler and then the profiler will respond with the appropriate ELT setting. In this way the ELT has logically become part of the profiler ReJIT stage configuration even though the runtime has no explicit record of it. Generalizing from this example, the runtime expects that all unrecorded code version configuration can be treated as a hidden portion of the configuration that is supplied by one of the explicitly configured build stages.
The runtime's current classification is:
diff --git a/Documentation/design-docs/inlining-plans.md b/Documentation/design-docs/inlining-plans.md
index 2279c16e37..79116092e9 100644
--- a/Documentation/design-docs/inlining-plans.md
+++ b/Documentation/design-docs/inlining-plans.md
@@ -31,12 +31,12 @@ overhead. It is anticipated that this work will encompass Machinery,
Ability, and Profitability.
LLILC does no inlining today. Since we aspire to have LLILC be a
-high-performance .Net code generator, we need to enable inlining in
+high-performance .NET code generator, we need to enable inlining in
LLILC. LLILC can likely leverage much of LLVM's built-in inlining
Machinery and Ability, but will need new code for Legality and
Profitability.
-We envision various scenarios for .Net Code generation that impact
+We envision various scenarios for .NET Code generation that impact
inlining: a first-tier JIT compiler, a higher-tier JIT compiler, a
fast AOT compiler, and an optimizing AOT compiler. Each scenario calls
for inlining, but the tradeoffs are different. For a given scenario,
@@ -221,7 +221,7 @@ xml or json markup):
```
where `[o]` is a successful inline, `[x]` a failed inline, and
-indentation shows the inlining tree. For .Net compilation we'll need
+indentation shows the inlining tree. For .NET compilation we'll need
some kind of persistent ID for methods, which may not be all that easy
to come by.
diff --git a/Documentation/design-docs/tiered-compilation.md b/Documentation/design-docs/tiered-compilation.md
index fbea857e50..70bb014338 100644
--- a/Documentation/design-docs/tiered-compilation.md
+++ b/Documentation/design-docs/tiered-compilation.md
@@ -40,11 +40,11 @@ Design
## Historical context ##
-Tiered Compilation was prototyped in 2016, introduced into the runtime code in 2017, and offered as an opt-in Preview feature in .Net Core 2.1 RTM in 2018. This design doc was written after the fact. We had been trying to mitigate runtime startup and performance problems for nearly 20 years with various forms of pre-compilation (NGEN, ReadyToRun, MulticoreJit) but this was the first serious foray into using compilation tiers to achieve similar goals. The IL interpreter appears similar but as best I understand it was not primarily targeted at performance, but rather at portability into environments that did not allow jitting. Although the idea of tiered compilation had come up repeatedly in the past it had never gained the degree of consensus/acceptance/momentum necessary to move forward relative to other performance investments.
+Tiered Compilation was prototyped in 2016, introduced into the runtime code in 2017, and offered as an opt-in Preview feature in .NET Core 2.1 RTM in 2018. This design doc was written after the fact. We had been trying to mitigate runtime startup and performance problems for nearly 20 years with various forms of pre-compilation (NGEN, ReadyToRun, MulticoreJit) but this was the first serious foray into using compilation tiers to achieve similar goals. The IL interpreter appears similar but as best I understand it was not primarily targeted at performance, but rather at portability into environments that did not allow jitting. Although the idea of tiered compilation had come up repeatedly in the past it had never gained the degree of consensus/acceptance/momentum necessary to move forward relative to other performance investments.
## Goals ##
-1. Improve the steady state and startup performance of typical .Net Core workloads while minimizing regressions.
+1. Improve the steady state and startup performance of typical .NET Core workloads while minimizing regressions.
2. Compliment existing precompilation techniques so that developers can leverage the best of both options.
diff --git a/Documentation/project-docs/jit-testing.md b/Documentation/project-docs/jit-testing.md
index bebcf2106e..a4a560b577 100644
--- a/Documentation/project-docs/jit-testing.md
+++ b/Documentation/project-docs/jit-testing.md
@@ -25,7 +25,7 @@ all OS platforms.
TBD).
5. Tests in CI can be run on private changes (currently tied to PRs; this may
be sufficient).
-6. Test strategy harmonious with other .Net repo test strategies.
+6. Test strategy harmonious with other .NET repo test strategies.
7. Test harness behaves reasonably on test failure. Easy to get at repro steps
for subsequent debugging.
8. Tests must allow fine-grained inspection of JIT outputs, for instance
@@ -58,7 +58,7 @@ formatting).
2. Some have deep desktop CLR dependence (testing a desktop CLR feature that
is not present in CoreCLR).
3. Some require tools not yet available in CoreCLR (ilasm in particular).
-4. Some test windows features and wont be relevant to other OS platforms.
+4. Some test windows features and won't be relevant to other OS platforms.
5. Some tests may not be able to be freely redistributed.
We have done an internal inventory and identified roughly 1000 tests that
@@ -68,7 +68,7 @@ moving these.
### Test script capabilities
We need to ensure that the CoreCLR repo contains a suitably
-hookable test script. Core testing is driven by xunit but theres typically a
+hookable test script. Core testing is driven by xunit but there's typically a
wrapper around this (runtest.cmd today) to facilitate test execution.
The proposal is to implement platform-neutral variant of runtest.cmd that
diff --git a/build-test.sh b/build-test.sh
index fe86703f56..d845185bcd 100755
--- a/build-test.sh
+++ b/build-test.sh
@@ -436,7 +436,12 @@ build_native_projects()
pushd "$intermediatesForBuild"
# Regenerate the CMake solution
# Force cross dir to point to project root cross dir, in case there is a cross build.
- nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__TestDir\" $__ClangMajorVersion $__ClangMinorVersion $platformArch $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ scriptDir="$__ProjectRoot/src/pal/tools"
+ if [[ $__GccBuild == 0 ]]; then
+ nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$scriptDir/gen-buildsys-clang.sh\" \"$__TestDir\" $__ClangMajorVersion $__ClangMinorVersion $platformArch $scriptDir $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ else
+ nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$scriptDir/gen-buildsys-gcc.sh\" \"$__TestDir\" \"$__GccMajorVersion\" \"$__GccMinorVersion\" $platformArch $scriptDir $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ fi
echo "Invoking $nextCommand"
eval $nextCommand
popd
@@ -475,6 +480,7 @@ usage()
echo "coverage - optional argument to enable code coverage build (currently supported only for Linux and OSX)."
echo "ninja - target ninja instead of GNU make"
echo "clangx.y - optional argument to build using clang version x.y - supported version 3.5 - 6.0"
+ echo "gccx.y - optional argument to build using gcc version x.y."
echo "cross - optional argument to signify cross compilation,"
echo " - will use ROOTFS_DIR environment variable if set."
echo "portableLinux - build for Portable Linux Distribution"
@@ -601,6 +607,9 @@ __ConfigureOnly=0
__CrossBuild=0
__ClangMajorVersion=0
__ClangMinorVersion=0
+__GccBuild=0
+__GccMajorVersion=0
+__GccMinorVersion=0
__NuGetPath="$__PackagesDir/NuGet.exe"
__SkipRestorePackages=0
__DistroRid=""
@@ -726,6 +735,36 @@ while :; do
__ClangMinorVersion=0
;;
+ gcc5|-gcc5)
+ __GccMajorVersion=5
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc6|-gcc6)
+ __GccMajorVersion=6
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc7|-gcc7)
+ __GccMajorVersion=7
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc8|-gcc8)
+ __GccMajorVersion=8
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc|-gcc)
+ __GccMajorVersion=
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
ninja)
__UseNinja=1
;;
diff --git a/build.sh b/build.sh
index 71b07fa2e8..7e9ce99124 100755
--- a/build.sh
+++ b/build.sh
@@ -276,16 +276,17 @@ build_native()
pushd "$intermediatesForBuild"
# Regenerate the CMake solution
+ scriptDir="$__ProjectRoot/src/pal/tools"
if [[ $__GccBuild == 0 ]]; then
scan_build=
if [[ $__StaticAnalyzer == 1 ]]; then
scan_build=scan-build
fi
- echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion \"$__ClangMinorVersion\" $platformArch $__BuildType $__CodeCoverage $scan_build $generator $extraCmakeArguments $__cmakeargs"
- source "$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion "$__ClangMinorVersion" $platformArch $__BuildType $__CodeCoverage $scan_build $generator "$extraCmakeArguments" "$__cmakeargs"
+ echo "Invoking \"$scriptDir/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion \"$__ClangMinorVersion\" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $scan_build $generator $extraCmakeArguments $__cmakeargs"
+ source "$scriptDir/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion "$__ClangMinorVersion" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $scan_build $generator "$extraCmakeArguments" "$__cmakeargs"
else
- echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-gcc.sh\" \"$__ProjectRoot\" $__GccMajorVersion \"$__GccMinorVersion\" $platformArch $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
- source "$__ProjectRoot/src/pal/tools/gen-buildsys-gcc.sh" "$__ProjectRoot" "$__GccMajorVersion" "$__CGccMinorVersion" $platformArch $__BuildType $__CodeCoverage $generator "$extraCmakeArguments" "$__cmakeargs"
+ echo "Invoking \"$scriptDir/gen-buildsys-gcc.sh\" \"$__ProjectRoot\" $__GccMajorVersion \"$__GccMinorVersion\" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ source "$scriptDir/gen-buildsys-gcc.sh" "$__ProjectRoot" "$__GccMajorVersion" "$__CGccMinorVersion" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $generator "$extraCmakeArguments" "$__cmakeargs"
fi
popd
fi
@@ -788,12 +789,24 @@ while :; do
__GccBuild=1
;;
+ gcc6|-gcc6)
+ __GccMajorVersion=6
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
gcc7|-gcc7)
__GccMajorVersion=7
__GccMinorVersion=
__GccBuild=1
;;
+ gcc8|-gcc8)
+ __GccMajorVersion=8
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
gcc|-gcc)
__GccMajorVersion=
__GccMinorVersion=
diff --git a/clr.defines.targets b/clr.defines.targets
index 3fa0417f51..e2f10586f1 100644
--- a/clr.defines.targets
+++ b/clr.defines.targets
@@ -1,6 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Features we're currently flighting, but don't intend to ship in officially supported releases -->
<PropertyGroup Condition="'$(IsPrerelease)' == 'true'">
+ <FeatureUtf8String>true</FeatureUtf8String>
<!-- FeatureXXX>true</FeatureXXX -->
</PropertyGroup>
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake
index 9e22da2033..a25d19d130 100644
--- a/clrdefinitions.cmake
+++ b/clrdefinitions.cmake
@@ -6,6 +6,7 @@ set(PRERELEASE 1)
# Features we're currently flighting, but don't intend to ship in officially supported releases
if (PRERELEASE)
+ add_definitions(-DFEATURE_UTF8STRING=1)
# add_definitions(-DFEATURE_XXX=1)
endif (PRERELEASE)
diff --git a/configurecompiler.cmake b/configurecompiler.cmake
index 39c2e7e410..c893e44282 100644
--- a/configurecompiler.cmake
+++ b/configurecompiler.cmake
@@ -485,6 +485,7 @@ if (CLR_CMAKE_PLATFORM_UNIX)
add_compile_options(-Wno-unused-variable)
add_compile_options(-Wno-unused-but-set-variable)
add_compile_options(-fms-extensions)
+ add_compile_options(-Wno-unknown-pragmas)
endif()
# Some architectures (e.g., ARM) assume char type is unsigned while CoreCLR assumes char is signed
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 60f3a71c1e..debcacfa39 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -3,31 +3,31 @@
<ProductDependencies>
</ProductDependencies>
<ToolsetDependencies>
- <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19160.2">
+ <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19167.10">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>89ab8b2b806397e5e444809a6ac12e275e0e20a2</Sha>
+ <Sha>de7be3ba62b92e5c48c36876c851a14f154444af</Sha>
</Dependency>
- <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19160.2">
+ <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19167.10">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>89ab8b2b806397e5e444809a6ac12e275e0e20a2</Sha>
+ <Sha>de7be3ba62b92e5c48c36876c851a14f154444af</Sha>
</Dependency>
- <Dependency Name="Microsoft.Private.CoreFx.NETCoreApp" Version="4.6.0-preview4.19163.10">
+ <Dependency Name="Microsoft.Private.CoreFx.NETCoreApp" Version="4.6.0-preview4.19164.7">
<Uri>https://github.com/dotnet/corefx</Uri>
- <Sha>689e58ec123d8c730d48d1c35c8f7ecbba6c22cf</Sha>
+ <Sha>d6a30736858f91b297fdd3ed4e3d1dfde67bdbdb</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.Platforms" Version="3.0.0-preview4.19163.10">
+ <Dependency Name="Microsoft.NETCore.Platforms" Version="3.0.0-preview4.19164.7">
<Uri>https://github.com/dotnet/corefx</Uri>
- <Sha>689e58ec123d8c730d48d1c35c8f7ecbba6c22cf</Sha>
+ <Sha>d6a30736858f91b297fdd3ed4e3d1dfde67bdbdb</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.App" Version="3.0.0-preview4-27511-01">
+ <Dependency Name="Microsoft.NETCore.App" Version="3.0.0-preview4-27518-07">
<Uri>https://github.com/dotnet/core-setup</Uri>
- <Sha>10b856b0402bc70f2190baa22df989bc0e9dac99</Sha>
+ <Sha>539e581a0d9ba313fa6edb12a3ed9fd3ba88e382</Sha>
</Dependency>
- <Dependency Name="optimization.IBC.CoreCLR" Version="99.99.99-master-20190314.1">
+ <Dependency Name="optimization.IBC.CoreCLR" Version="99.99.99-master-20190313.3">
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
<Sha>4f9d0ecf2b8151859fd2bd0734af32ea59258a3d</Sha>
</Dependency>
- <Dependency Name="optimization.PGO.CoreCLR" Version="99.99.99-master-20190314.1">
+ <Dependency Name="optimization.PGO.CoreCLR" Version="99.99.99-master-20190313.3">
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
<Sha>4f9d0ecf2b8151859fd2bd0734af32ea59258a3d</Sha>
</Dependency>
diff --git a/eng/Versions.props b/eng/Versions.props
index 50cf60d51d..1fc89c618a 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -8,11 +8,11 @@
<UsingToolXliff>false</UsingToolXliff>
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
<MicrosoftNetFrameworkReferenceAssembliesVersion>1.0.0-alpha-004</MicrosoftNetFrameworkReferenceAssembliesVersion>
- <MicrosoftPrivateCoreFxNETCoreAppVersion>4.6.0-preview4.19163.10</MicrosoftPrivateCoreFxNETCoreAppVersion>
- <MicrosoftNETCorePlatformsVersion>3.0.0-preview4.19163.10</MicrosoftNETCorePlatformsVersion>
- <MicrosoftNETCoreAppVersion>3.0.0-preview4-27511-01</MicrosoftNETCoreAppVersion>
- <optimizationIBCCoreCLRVersion>99.99.99-master-20190314.1</optimizationIBCCoreCLRVersion>
- <optimizationPGOCoreCLRVersion>99.99.99-master-20190314.1</optimizationPGOCoreCLRVersion>
+ <MicrosoftPrivateCoreFxNETCoreAppVersion>4.6.0-preview4.19164.7</MicrosoftPrivateCoreFxNETCoreAppVersion>
+ <MicrosoftNETCorePlatformsVersion>3.0.0-preview4.19164.7</MicrosoftNETCorePlatformsVersion>
+ <MicrosoftNETCoreAppVersion>3.0.0-preview4-27518-07</MicrosoftNETCoreAppVersion>
+ <optimizationIBCCoreCLRVersion>99.99.99-master-20190313.3</optimizationIBCCoreCLRVersion>
+ <optimizationPGOCoreCLRVersion>99.99.99-master-20190313.3</optimizationPGOCoreCLRVersion>
</PropertyGroup>
<!--Package names-->
<PropertyGroup>
diff --git a/eng/common/PublishToPackageFeed.proj b/eng/common/PublishToPackageFeed.proj
index b26d28a90b..25362ff060 100644
--- a/eng/common/PublishToPackageFeed.proj
+++ b/eng/common/PublishToPackageFeed.proj
@@ -11,11 +11,17 @@
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)DefaultVersions.props" Condition="Exists('$(MSBuildThisFileDirectory)DefaultVersions.props')" />
+
+ <!--
+ This won't be necessary once we solve this issue:
+ https://github.com/dotnet/arcade/issues/2266
+ -->
+ <Import Project="$(MSBuildThisFileDirectory)ArtifactsCategory.props" Condition="Exists('$(MSBuildThisFileDirectory)ArtifactsCategory.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.dotnet.build.tasks.feed\$(MicrosoftDotNetBuildTasksFeedVersion)\build\Microsoft.DotNet.Build.Tasks.Feed.targets" />
<Target Name="PublishToFeed">
- <Error Condition="'$(TargetStaticFeed)' == ''" Text="TargetStaticFeed: Target feed for publishing assets wasn't provided." />
+ <Error Condition="'$(ArtifactsCategory)' == ''" Text="ArtifactsCategory: The artifacts' category produced by the build wasn't provided." />
<Error Condition="'$(AccountKeyToStaticFeed)' == ''" Text="AccountKeyToStaticFeed: Account key for target feed wasn't provided." />
<Error Condition="'$(ManifestsBasePath)' == ''" Text="Full path to asset manifests directory wasn't provided." />
<Error Condition="'$(BlobBasePath)' == '' AND '$(PackageBasePath)' == ''" Text="A valid full path to BlobBasePath of PackageBasePath is required." />
@@ -26,7 +32,21 @@
</ItemGroup>
<Error Condition="'@(ManifestFiles)' == ''" Text="No manifest file was found in the provided path: $(ManifestsBasePath)" />
-
+
+ <!--
+ For now the type of packages being published will be informed for the whole build.
+ Eventually this will be specified on a per package basis:
+ TODO: https://github.com/dotnet/arcade/issues/2266
+ -->
+ <PropertyGroup>
+ <TargetStaticFeed Condition="'$(ArtifactsCategory.ToUpper())' == '.NETCORE'">https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json</TargetStaticFeed>
+ <TargetStaticFeed Condition="'$(ArtifactsCategory.ToUpper())' == '.NETCOREVALIDATION'">https://dotnetfeed.blob.core.windows.net/arcade-validation/index.json</TargetStaticFeed>
+ </PropertyGroup>
+
+ <Error
+ Condition="'$(TargetStaticFeed)' == ''"
+ Text="'$(ArtifactsCategory)' wasn't recognized as a valid artifact category. Valid categories are: '.NetCore' and '.NetCoreValidation'" />
+
<!-- Iterate publishing assets from each manifest file. -->
<PushArtifactsInManifestToFeed
ExpectedFeedUrl="$(TargetStaticFeed)"
diff --git a/eng/common/generate-graph-files.ps1 b/eng/common/generate-graph-files.ps1
index c04c80e4f6..e09c64e9f6 100644
--- a/eng/common/generate-graph-files.ps1
+++ b/eng/common/generate-graph-files.ps1
@@ -42,7 +42,7 @@ try {
}
Write-Host "Generating dependency graph..."
- $darc = Invoke-Expression "& `"$darcExe`" $options"
+ Invoke-Expression "& `"$darcExe`" $options"
CheckExitCode "Generating dependency graph"
$graph = Get-Content $graphVizFilePath
diff --git a/eng/common/init-tools-native.ps1 b/eng/common/init-tools-native.ps1
index e25c60fed4..495a563a75 100644
--- a/eng/common/init-tools-native.ps1
+++ b/eng/common/init-tools-native.ps1
@@ -98,10 +98,20 @@ try {
Write-Verbose "Executing '$LocalInstallerCommand'"
Invoke-Expression "$LocalInstallerCommand"
if ($LASTEXITCODE -Ne "0") {
- Write-Error "Execution failed"
- exit 1
+ $errMsg = "$ToolName installation failed"
+ if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) {
+ Write-Warning $errMsg
+ $toolInstallationFailure = $true
+ } else {
+ Write-Error $errMsg
+ exit 1
+ }
}
}
+
+ if ((Get-Variable 'toolInstallationFailure' -ErrorAction 'SilentlyContinue') -and $toolInstallationFailure) {
+ exit 1
+ }
}
else {
Write-Host "No native tools defined in global.json"
diff --git a/eng/common/templates/job/generate-graph-files.yml b/eng/common/templates/job/generate-graph-files.yml
new file mode 100644
index 0000000000..e54ce956f9
--- /dev/null
+++ b/eng/common/templates/job/generate-graph-files.yml
@@ -0,0 +1,48 @@
+parameters:
+ # Optional: dependencies of the job
+ dependsOn: ''
+
+ # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool
+ pool: {}
+
+ # Optional: Include toolset dependencies in the generated graph files
+ includeToolset: false
+
+jobs:
+- job: Generate_Graph_Files
+
+ dependsOn: ${{ parameters.dependsOn }}
+
+ displayName: Generate Graph Files
+
+ pool: ${{ parameters.pool }}
+
+ variables:
+ # Publish-Build-Assets provides: MaestroAccessToken, BotAccount-dotnet-maestro-bot-PAT
+ # DotNet-AllOrgs-Darc-Pats provides: dn-bot-devdiv-dnceng-rw-code-pat
+ - group: Publish-Build-Assets
+ - group: DotNet-AllOrgs-Darc-Pats
+ - name: _GraphArguments
+ value: -gitHubPat $(BotAccount-dotnet-maestro-bot-PAT)
+ -azdoPat $(dn-bot-devdiv-dnceng-rw-code-pat)
+ -barToken $(MaestroAccessToken)
+ -outputFolder '$(Build.StagingDirectory)/GraphFiles/'
+ - ${{ if ne(parameters.includeToolset, 'false') }}:
+ - name: _GraphArguments
+ value: ${{ variables._GraphArguments }} -includeToolset
+
+ steps:
+ - task: PowerShell@2
+ displayName: Generate Graph Files
+ inputs:
+ filePath: eng\common\generate-graph-files.ps1
+ arguments: $(_GraphArguments)
+ continueOnError: true
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Graph to Artifacts
+ inputs:
+ PathtoPublish: '$(Build.StagingDirectory)/GraphFiles'
+ PublishLocation: Container
+ ArtifactName: GraphFiles
+ continueOnError: true
+ condition: always()
diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml
index d6d8697cbd..83646c6438 100644
--- a/eng/common/templates/job/publish-build-assets.yml
+++ b/eng/common/templates/job/publish-build-assets.yml
@@ -52,6 +52,7 @@ jobs:
/p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests'
/p:BuildAssetRegistryToken=$(MaestroAccessToken)
/p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com
+ /p:PublishUsingPipelines=$(_PublishUsingPipelines)
/p:Configuration=$(_BuildConfig)
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml
index c1a5b4849a..06ed58de41 100644
--- a/eng/common/templates/jobs/jobs.yml
+++ b/eng/common/templates/jobs/jobs.yml
@@ -13,7 +13,13 @@ parameters:
# Optional: Enable publishing to the build asset registry
enablePublishBuildAssets: false
-
+
+ graphFileGeneration:
+ # Optional: Enable generating the graph files at the end of the build
+ enabled: false
+ # Optional: Include toolset dependencies in the generated graph files
+ includeToolset: false
+
# Optional: Include PublishTestResults task
enablePublishTestResults: false
@@ -68,4 +74,13 @@ jobs:
vmImage: vs2017-win2016
runAsPublic: ${{ parameters.runAsPublic }}
enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
-
+
+- ${{ if and(eq(parameters.graphFileGeneration.enabled, true), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - template: ../job/generate-graph-files.yml
+ parameters:
+ continueOnError: ${{ parameters.continueOnError }}
+ includeToolset: ${{ parameters.graphFileGeneration.includeToolset }}
+ dependsOn:
+ - Asset_Registry_Publish
+ pool:
+ vmImage: vs2017-win2016
diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml
index 0925e8ebd1..45d63fbc38 100644
--- a/eng/common/templates/steps/send-to-helix.yml
+++ b/eng/common/templates/steps/send-to-helix.yml
@@ -2,7 +2,7 @@ parameters:
HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/
HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/'
HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number
- HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/api/2018-03-14/info/queues for a list of queues
+ HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues
HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group
HelixPreCommands: '' # optional -- commands to run before Helix work item execution
HelixPostCommands: '' # optional -- commands to run after Helix work item execution
diff --git a/eng/platform-matrix.yml b/eng/platform-matrix.yml
index 69d4cbe15f..1c2b3ea0b2 100644
--- a/eng/platform-matrix.yml
+++ b/eng/platform-matrix.yml
@@ -33,9 +33,9 @@ jobs:
containerName: ubuntu_1404_arm_cross_build_image
helixQueues:
${{ if eq(variables['System.TeamProject'], 'public') }}:
- # TODO: add Ubuntu.1404.Arm32.Open once Jenkins has been shutdown
- asString: ''
- asArray: []
+ asString: 'Ubuntu.1404.Arm32.Open'
+ asArray:
+ - Ubuntu.1404.Arm32.Open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
# We don't have any Linux/arm32 internal Helix queues
asString: ''
diff --git a/global.json b/global.json
index 7d183ef805..74eeacf969 100644
--- a/global.json
+++ b/global.json
@@ -7,7 +7,7 @@
"python": "2.7.15"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19160.2",
- "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19160.2"
+ "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19167.10",
+ "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19167.10"
}
}
diff --git a/netci.groovy b/netci.groovy
index 6ab04375fb..d9f42816e1 100755
--- a/netci.groovy
+++ b/netci.groovy
@@ -169,58 +169,17 @@ class Constants {
// Valid PR trigger combinations.
def static prTriggeredValidInnerLoopCombos = [
'Windows_NT': [
- 'x64': [
- 'Checked'
- ],
- 'x86': [
- 'Checked',
- 'Release'
- ],
'arm': [
- 'Debug',
'Checked'
],
'arm64': [
- 'Debug',
'Checked'
]
],
'Windows_NT_BuildOnly': [
- 'x64': [
- 'Checked',
- 'Release'
- ],
- 'x86': [
- 'Checked',
- 'Release'
- ],
'arm': [
'Checked'
],
- ],
- 'Ubuntu': [
- 'x64': [
- 'Checked'
- ],
- 'arm': [
- 'Checked'
- ]
- ],
- 'Ubuntu16.04': [
- 'arm64': [
- 'Checked'
- ]
- ],
- 'CentOS7.1': [
- 'x64': [
- 'Debug',
- 'Checked'
- ]
- ],
- 'OSX10.12': [
- 'x64': [
- 'Checked'
- ]
]
]
@@ -679,8 +638,9 @@ def static addPeriodicTriggerHelper(def job, String cronString, boolean alwaysRu
}
def static addGithubPushTriggerHelper(def job) {
- addToMergeView(job)
- Utilities.addGithubPushTrigger(job)
+ // Disable all Push trigger jobs. All jobs will need to be requested.
+ // addToMergeView(job)
+ // Utilities.addGithubPushTrigger(job)
}
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 536977169d..0cf6733ad9 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -112,6 +112,10 @@
<!-- CLR Features -->
<Import Project="$(MSBuildThisFileDirectory)..\..\clr.coreclr.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\clr.defines.targets" />
+ <!-- Experimental features -->
+ <PropertyGroup Condition="'$(FeatureUtf8String)' == 'true'">
+ <DefineConstants>$(DefineConstants);FEATURE_UTF8STRING</DefineConstants>
+ </PropertyGroup>
<!-- Sources -->
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
@@ -152,7 +156,7 @@
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\ISymWriter.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\SymAddressKind.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\Token.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Enum.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Enum.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Environment.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Exception.cs" />
<Compile Include="$(BclSourcesRoot)\System\GC.cs" />
@@ -274,6 +278,14 @@
<Compile Include="shared\Interop\Windows\Ole32\Interop.CoTaskMemAlloc.cs" />
<Compile Include="shared\Interop\Windows\OleAut32\Interop.SysAllocStringByteLen.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(FeatureUtf8String)' == 'true'">
+ <Compile Include="$(BclSourcesRoot)\System\Char8.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8Extensions.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Construction.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Manipulation.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Searching.cs" />
+ </ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\XplatEventLogger.cs" Condition="'$(FeatureXplatEventSource)' == 'true'" />
<Compile Include="$(IntermediateOutputPath)..\Eventing\NativeRuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'"/>
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 e8baac9747..4b5bd02062 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -223,6 +223,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.SpecialFolder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.SpecialFolderOption.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EnvironmentVariableTarget.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Enum.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EventHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ExecutionEngineException.cs" />
@@ -359,6 +360,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Fast.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Trim.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingFieldException.cs" />
@@ -429,6 +431,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\BindingFlags.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\CallingConventions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ConstructorInfo.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\CorElementType.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\CustomAttributeFormatException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\DefaultMemberAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Emit\AssemblyBuilderAccess.cs" />
@@ -799,6 +802,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF8Encoding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\ValueStringBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf8.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf8Utility.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeSpan.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ThreadAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\AbandonedMutexException.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs
index e7dd90d4b0..ae4a1a7280 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterPayload.cs
@@ -124,7 +124,7 @@ namespace System.Diagnostics.Tracing
public string DisplayName { get; set; }
- public TimeSpan DisplayRateTimeScale { get; set; }
+ public string DisplayRateTimeScale { get; set; }
public float Increment { get; set; }
@@ -150,7 +150,7 @@ namespace System.Diagnostics.Tracing
{
yield return new KeyValuePair<string, object>("Name", Name);
yield return new KeyValuePair<string, object>("DisplayName", DisplayName);
- yield return new KeyValuePair<string, object>("DisplayRateTimeScale", DisplayRateTimeScale.ToString("c"));
+ yield return new KeyValuePair<string, object>("DisplayRateTimeScale", DisplayRateTimeScale);
yield return new KeyValuePair<string, object>("Increment", Increment);
yield return new KeyValuePair<string, object>("IntervalSec", IntervalSec);
yield return new KeyValuePair<string, object>("Series", $"Interval={IntervalSec}");
@@ -161,4 +161,4 @@ namespace System.Diagnostics.Tracing
#endregion // Implementation of the IEnumerable interface
}
-} \ No newline at end of file
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
index 05f6d43894..569c9a0113 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
@@ -62,8 +62,8 @@ namespace System.Diagnostics.Tracing
IncrementingCounterPayload payload = new IncrementingCounterPayload();
payload.Name = _name;
payload.IntervalSec = intervalSec;
- payload.DisplayName = DisplayName;
- payload.DisplayRateTimeScale = DisplayRateTimeScale;
+ payload.DisplayName = DisplayName ?? "";
+ payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c");
payload.MetaData = GetMetaDataString();
payload.Increment = _increment - _prevIncrement;
_prevIncrement = _increment;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
index d9f640c73d..b5a2582083 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
@@ -71,8 +71,8 @@ namespace System.Diagnostics.Tracing
{
IncrementingCounterPayload payload = new IncrementingCounterPayload();
payload.Name = _name;
- payload.DisplayName = DisplayName;
- payload.DisplayRateTimeScale = DisplayRateTimeScale;
+ payload.DisplayName = DisplayName ?? "";
+ payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c");
payload.IntervalSec = intervalSec;
payload.MetaData = GetMetaDataString();
payload.Increment = _increment - _prevIncrement;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
index 364e50dc3d..695b357f45 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
@@ -58,6 +58,7 @@ namespace System.Diagnostics.Tracing
CounterPayload payload = new CounterPayload();
payload.Name = _name;
+ payload.DisplayName = DisplayName ?? "";
payload.Count = 1; // NOTE: These dumb-looking statistics is intentional
payload.IntervalSec = intervalSec;
payload.Mean = value;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
index daa6d9736a..0f87ea58db 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
@@ -185,7 +185,7 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// Gets a delegate that gets the value of a property of a value type. We unfortunately cannot avoid boxing the value type,
/// without making this generic over the value type. That would result in a large number of generic instantiations, and furthermore
- /// does not work correctly on .Net Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined
+ /// does not work correctly on .NET Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined
/// value types will be rare, and in any case the boxing only happens for events that are actually enabled.
/// </summary>
private static Func<PropertyValue, PropertyValue> GetBoxedValueTypePropertyGetter(PropertyInfo property)
@@ -202,7 +202,7 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// For properties of reference types, we use a generic helper class to get the value. This enables us to use MethodInfo.CreateDelegate
- /// to build a fast getter. We can get away with this on .Net Native, because we really only need one runtime instantiation of the
+ /// to build a fast getter. We can get away with this on .NET Native, because we really only need one runtime instantiation of the
/// generic type, since it's only instantiated over reference types (and thus all instances are shared).
/// </summary>
/// <param name="property"></param>
diff --git a/src/System.Private.CoreLib/src/System/Enum.cs b/src/System.Private.CoreLib/shared/System/Enum.cs
index 93c91f2468..444367cebf 100644
--- a/src/System.Private.CoreLib/src/System/Enum.cs
+++ b/src/System.Private.CoreLib/shared/System/Enum.cs
@@ -6,10 +6,13 @@ using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
using Internal.Runtime.CompilerServices;
+#if CORERT
+using CorElementType = System.Runtime.RuntimeImports.RhCorElementType;
+using RuntimeType = System.Type;
+#endif
+
// The code below includes partial support for float/double and
// pointer sized enums.
//
@@ -26,34 +29,13 @@ namespace System
{
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
+ public abstract partial class Enum : ValueType, IComparable, IFormattable, IConvertible
{
#region Private Constants
private const char EnumSeparatorChar = ',';
#endregion
#region Private Static Methods
- private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
- {
- TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
-
- if (entry == null || (getNames && entry.Names == null))
- {
- ulong[] values = null;
- string[] names = null;
- GetEnumValuesAndNames(
- enumType.GetTypeHandleInternal(),
- JitHelpers.GetObjectHandleOnStack(ref values),
- JitHelpers.GetObjectHandleOnStack(ref names),
- getNames);
- bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
-
- entry = new TypeValuesAndNames(isFlags, values, names);
- enumType.GenericCache = entry;
- }
-
- return entry;
- }
private string ValueToString()
{
@@ -153,12 +135,12 @@ namespace System
internal static string GetEnumName(RuntimeType eT, ulong ulValue)
{
Debug.Assert(eT != null);
- ulong[] ulValues = Enum.InternalGetValues(eT);
+ ulong[] ulValues = InternalGetValues(eT);
int index = Array.BinarySearch(ulValues, ulValue);
if (index >= 0)
{
- string[] names = Enum.InternalGetNames(eT);
+ string[] names = InternalGetNames(eT);
return names[index];
}
@@ -174,7 +156,7 @@ namespace System
if (!entry.IsFlag) // Not marked with Flags attribute
{
- return Enum.GetEnumName(eT, value);
+ return GetEnumName(eT, value);
}
else // These are flags OR'ed together (We treat everything as unsigned types)
{
@@ -336,17 +318,6 @@ namespace System
return result;
}
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private static extern int InternalCompareTo(object o1, object o2);
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern void GetEnumValuesAndNames(RuntimeTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, bool getNames);
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private static extern object InternalBoxEnum(RuntimeType enumType, long value);
#endregion
#region Public Static Methods
@@ -811,44 +782,12 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool StartsNumber(char c) => char.IsInRange(c, '0', '9') || c == '-' || c == '+';
- public static Type GetUnderlyingType(Type enumType)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
-
- return enumType.GetEnumUnderlyingType();
- }
-
- public static Array GetValues(Type enumType)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
-
- return enumType.GetEnumValues();
- }
-
internal static ulong[] InternalGetValues(RuntimeType enumType)
{
// Get all of the values
return GetCachedValuesAndNames(enumType, false).Values;
}
- public static string GetName(Type enumType, object value)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
-
- return enumType.GetEnumName(value);
- }
-
- public static string[] GetNames(Type enumType)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
-
- return enumType.GetEnumNames();
- }
-
internal static string[] InternalGetNames(RuntimeType enumType)
{
// Get all of the names
@@ -918,7 +857,7 @@ namespace System
if (format == null)
throw new ArgumentNullException(nameof(format));
-
+
// If the value is an Enum then we need to extract the underlying value from it
Type valueType = value.GetType();
if (valueType.IsEnum)
@@ -933,7 +872,7 @@ namespace System
}
return ((Enum)value).ToString(format);
}
-
+
// The value must be of the same type as the Underlying type of the Enum
Type underlyingType = GetUnderlyingType(enumType);
if (valueType != underlyingType)
@@ -1060,17 +999,9 @@ namespace System
}
}
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern bool InternalHasFlag(Enum flags);
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern CorElementType InternalGetCorElementType();
-
#endregion
#region Object Overrides
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern override bool Equals(object obj);
public override int GetHashCode()
{
@@ -1207,23 +1138,9 @@ namespace System
return ToString();
}
- [Intrinsic]
- public bool HasFlag(Enum flag)
- {
- if (flag == null)
- throw new ArgumentNullException(nameof(flag));
-
- if (!this.GetType().IsEquivalentTo(flag.GetType()))
- {
- throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), this.GetType()));
- }
-
- return InternalHasFlag(flag);
- }
-
#endregion
- #region IConvertable
+ #region IConvertible
public TypeCode GetTypeCode()
{
switch (InternalGetCorElementType())
@@ -1364,16 +1281,6 @@ namespace System
private static object ToObject(Type enumType, bool value) =>
InternalBoxEnum(ValidateRuntimeType(enumType), value ? 1 : 0);
- private static RuntimeType ValidateRuntimeType(Type enumType)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
- if (!enumType.IsEnum)
- throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
- if (!(enumType is RuntimeType rtType))
- throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
- return rtType;
- }
#endregion
}
}
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 3702ee431f..403e0e3583 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs
@@ -365,7 +365,7 @@ namespace System.Globalization
//
// Get the native day names
//
- // NOTE: There's a disparity between .Net & windows day orders, the input day should
+ // NOTE: There's a disparity between .NET & windows day orders, the input day should
// start with Sunday
//
// Parameters:
diff --git a/src/System.Private.CoreLib/shared/System/Memory.cs b/src/System.Private.CoreLib/shared/System/Memory.cs
index ba31a6aeae..2074404630 100644
--- a/src/System.Private.CoreLib/shared/System/Memory.cs
+++ b/src/System.Private.CoreLib/shared/System/Memory.cs
@@ -6,6 +6,7 @@ using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
@@ -164,7 +165,13 @@ namespace System
// No validation performed in release builds; caller must provide any necessary validation.
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
- Debug.Assert((obj == null) || (typeof(T) == typeof(char) && obj is string) || (obj is T[]) || (obj is MemoryManager<T>));
+ Debug.Assert((obj == null)
+ || (typeof(T) == typeof(char) && obj is string)
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
+#endif // FEATURE_UTF8STRING
+ || (obj is T[])
+ || (obj is MemoryManager<T>));
_object = obj;
_index = start;
@@ -212,6 +219,14 @@ namespace System
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ Span<T> span = Span;
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length);
}
@@ -317,6 +332,13 @@ namespace System
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
+ {
+ refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
+ lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
@@ -427,6 +449,14 @@ namespace System
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
+ {
+ GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
+ ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
+ return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
new file mode 100644
index 0000000000..be98bb1fba
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
@@ -0,0 +1,866 @@
+// 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;
+
+namespace System
+{
+ public static partial class MemoryExtensions
+ {
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> Trim<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> Trim<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimStart<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimEnd<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Delimits all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+
+ if (trimElement != null)
+ {
+ for (; start < span.Length; start++)
+ {
+ if (!trimElement.Equals(span[start]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, T trimElement)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ if (trimElement != null)
+ {
+ for (; end >= start; end--)
+ {
+ if (!trimElement.Equals(span[end]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; end >= start; end--)
+ {
+ if (span[end] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> Trim<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> Trim<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimStart<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimEnd<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Delimits all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!trimElements.Contains(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+ for (; end >= start; end--)
+ {
+ if (!trimElements.Contains(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> Trim(this Memory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimStart(this Memory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimEnd(this Memory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> Trim(this ReadOnlyMemory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimStart(this ReadOnlyMemory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimEnd(this ReadOnlyMemory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ 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>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ 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 from the span.
+ /// </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> Trim(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (span[end] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified character from the span.
+ /// </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> 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 from the span.
+ /// </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>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ => 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>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimStart();
+ }
+
+ 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>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimEnd();
+ }
+
+ 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>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> Trim(this Span<char> span)
+ {
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimStart(this Span<char> span)
+ => span.Slice(ClampStart(span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimEnd(this Span<char> span)
+ => span.Slice(0, ClampEnd(span, 0));
+
+ /// <summary>
+ /// Delimits all leading occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ private static int ClampStart(ReadOnlySpan<char> span)
+ {
+ int start = 0;
+
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ private static int ClampEnd(ReadOnlySpan<char> span, int start)
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ for (; end >= start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
index 869123a81d..2b7861f3f5 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
@@ -23,178 +23,6 @@ namespace System
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)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (!char.IsWhiteSpace(span[start]))
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (!char.IsWhiteSpace(span[end]))
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <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>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (span[start] != trimChar)
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (span[end] != trimChar)
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <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>
- 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>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- 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>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimStart();
- }
-
- 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>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimEnd();
- }
-
- 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)
@@ -217,13 +45,13 @@ namespace System
public static bool Contains<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.Contains(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.Contains(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -242,13 +70,13 @@ namespace System
public static bool Contains<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.Contains(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.Contains(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -266,13 +94,13 @@ namespace System
public static int IndexOf<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -290,13 +118,13 @@ namespace System
public static int IndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -315,13 +143,13 @@ namespace System
public static int LastIndexOf<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -339,7 +167,7 @@ namespace System
public static int LastIndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -358,12 +186,15 @@ namespace System
{
int length = span.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
@@ -374,6 +205,9 @@ namespace System
public static int SequenceCompareTo<T>(this Span<T> span, ReadOnlySpan<T> other)
where T : IComparable<T>
{
+ // Can't use IsBitwiseEquatable<T>() below because that only tells us about
+ // equality checks, not about CompareTo checks.
+
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
@@ -400,13 +234,13 @@ namespace System
public static int IndexOf<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -424,13 +258,13 @@ namespace System
public static int IndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -449,13 +283,13 @@ namespace System
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value),
span.Length);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value),
@@ -473,7 +307,7 @@ namespace System
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -493,13 +327,13 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOfAny(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value0),
@@ -520,14 +354,14 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOfAny(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value0),
@@ -547,7 +381,7 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
{
ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
if (values.Length == 2)
@@ -576,7 +410,7 @@ namespace System
values.Length);
}
}
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
{
ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
if (values.Length == 5)
@@ -644,13 +478,13 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOfAny(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value0),
@@ -671,14 +505,14 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
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);
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.IndexOfAny(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, char>(ref value0),
@@ -698,7 +532,7 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
{
ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
if (values.Length == 2)
@@ -728,7 +562,7 @@ namespace System
}
}
- if (typeof(T) == typeof(char))
+ if (Unsafe.SizeOf<T>() == sizeof(char) && RuntimeHelpers.IsBitwiseEquatable<T>())
{
ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
if (values.Length == 5)
@@ -796,7 +630,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -817,7 +651,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -837,7 +671,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -857,7 +691,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -878,7 +712,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -898,7 +732,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -916,12 +750,15 @@ namespace System
where T : IEquatable<T>
{
int length = span.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
@@ -933,6 +770,9 @@ namespace System
public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
where T : IComparable<T>
{
+ // Can't use IsBitwiseEquatable<T>() below because that only tells us about
+ // equality checks, not about CompareTo checks.
+
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
@@ -958,12 +798,15 @@ namespace System
where T : IEquatable<T>
{
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
@@ -976,12 +819,15 @@ namespace System
where T : IEquatable<T>
{
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
@@ -995,12 +841,15 @@ namespace System
{
int spanLength = span.Length;
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
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)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
@@ -1018,12 +867,15 @@ namespace System
{
int spanLength = span.Length;
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
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)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
@@ -1705,36 +1557,5 @@ namespace System
value, comparer);
return BinarySearch(span, comparable);
}
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsTypeComparableAsBytes<T>(out nuint size)
- {
- if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte))
- {
- size = sizeof(byte);
- return true;
- }
-
- if (typeof(T) == typeof(char) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
- {
- size = sizeof(char);
- return true;
- }
-
- if (typeof(T) == typeof(int) || typeof(T) == typeof(uint))
- {
- size = sizeof(int);
- return true;
- }
-
- if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong))
- {
- size = sizeof(long);
- return true;
- }
-
- size = default;
- return false;
- }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
index 6c598430ad..bf90f0449d 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
@@ -6,6 +6,7 @@ using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
@@ -99,7 +100,13 @@ namespace System
// No validation performed in release builds; caller must provide any necessary validation.
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
- Debug.Assert((obj == null) || (typeof(T) == typeof(char) && obj is string) || (obj is T[]) || (obj is MemoryManager<T>));
+ Debug.Assert((obj == null)
+ || (typeof(T) == typeof(char) && obj is string)
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
+#endif // FEATURE_UTF8STRING
+ || (obj is T[])
+ || (obj is MemoryManager<T>));
_object = obj;
_index = start;
@@ -141,6 +148,14 @@ namespace System
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ ReadOnlySpan<T> span = Span;
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length);
}
@@ -239,6 +254,13 @@ namespace System
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
+ {
+ refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
+ lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
@@ -342,6 +364,14 @@ namespace System
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
+ {
+ GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
+ ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
+ return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
index eb3fd1464d..00337a5fd7 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using Internal.Runtime.CompilerServices;
@@ -240,12 +241,15 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- unsafe
- {
- fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
- return new string(src, 0, _length);
- }
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length);
}
diff --git a/src/System.Private.CoreLib/shared/System/Reflection/CorElementType.cs b/src/System.Private.CoreLib/shared/System/Reflection/CorElementType.cs
new file mode 100644
index 0000000000..37ffcfa1e2
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Reflection/CorElementType.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ internal enum CorElementType : byte
+ {
+ ELEMENT_TYPE_END = 0x00,
+ ELEMENT_TYPE_VOID = 0x01,
+ ELEMENT_TYPE_BOOLEAN = 0x02,
+ ELEMENT_TYPE_CHAR = 0x03,
+ ELEMENT_TYPE_I1 = 0x04,
+ ELEMENT_TYPE_U1 = 0x05,
+ ELEMENT_TYPE_I2 = 0x06,
+ ELEMENT_TYPE_U2 = 0x07,
+ ELEMENT_TYPE_I4 = 0x08,
+ ELEMENT_TYPE_U4 = 0x09,
+ ELEMENT_TYPE_I8 = 0x0A,
+ ELEMENT_TYPE_U8 = 0x0B,
+ ELEMENT_TYPE_R4 = 0x0C,
+ ELEMENT_TYPE_R8 = 0x0D,
+ ELEMENT_TYPE_STRING = 0x0E,
+ ELEMENT_TYPE_PTR = 0x0F,
+ ELEMENT_TYPE_BYREF = 0x10,
+ ELEMENT_TYPE_VALUETYPE = 0x11,
+ ELEMENT_TYPE_CLASS = 0x12,
+ ELEMENT_TYPE_VAR = 0x13,
+ ELEMENT_TYPE_ARRAY = 0x14,
+ ELEMENT_TYPE_GENERICINST = 0x15,
+ ELEMENT_TYPE_TYPEDBYREF = 0x16,
+ ELEMENT_TYPE_I = 0x18,
+ ELEMENT_TYPE_U = 0x19,
+ ELEMENT_TYPE_FNPTR = 0x1B,
+ ELEMENT_TYPE_OBJECT = 0x1C,
+ ELEMENT_TYPE_SZARRAY = 0x1D,
+ ELEMENT_TYPE_MVAR = 0x1E,
+ ELEMENT_TYPE_CMOD_REQD = 0x1F,
+ ELEMENT_TYPE_CMOD_OPT = 0x20,
+ ELEMENT_TYPE_INTERNAL = 0x21,
+ ELEMENT_TYPE_MAX = 0x22,
+ ELEMENT_TYPE_MODIFIER = 0x40,
+ ELEMENT_TYPE_SENTINEL = 0x41,
+ ELEMENT_TYPE_PINNED = 0x45,
+ }
+}
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 b1f5507122..225f434382 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
@@ -28,7 +28,12 @@ namespace System.Runtime.InteropServices
// As an optimization, we skip the "is string?" check below if typeof(T) is not char,
// as Memory<T> / ROM<T> can't possibly contain a string instance in this case.
- if (obj != null && (typeof(T) != typeof(char) || obj.GetType() != typeof(string)))
+ if (obj != null && !(
+ (typeof(T) == typeof(char) && obj.GetType() == typeof(string))
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj.GetType() == typeof(Utf8String))
+#endif // FEATURE_UTF8STRING
+ ))
{
if (RuntimeHelpers.ObjectHasComponentSize(obj))
{
diff --git a/src/System.Private.CoreLib/shared/System/Span.Fast.cs b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
index 66de4fe3d3..adc1f3903d 100644
--- a/src/System.Private.CoreLib/shared/System/Span.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using Internal.Runtime.CompilerServices;
@@ -319,12 +320,15 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- unsafe
- {
- fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
- return new string(src, 0, _length);
- }
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
+ }
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
}
+#endif // FEATURE_UTF8STRING
return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length);
}
diff --git a/src/System.Private.CoreLib/shared/System/String.cs b/src/System.Private.CoreLib/shared/System/String.cs
index 49afbc8c8c..10f75225c0 100644
--- a/src/System.Private.CoreLib/shared/System/String.cs
+++ b/src/System.Private.CoreLib/shared/System/String.cs
@@ -24,9 +24,13 @@ namespace System
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable<char>, IComparable<string>, IEquatable<string>, ICloneable
{
- // String constructors
- // These are special. The implementation methods for these have a different signature from the
- // declared constructors.
+ /*
+ * CONSTRUCTORS
+ *
+ * Defining a new constructor for string-like types (like String) requires changes both
+ * to the managed code below and to the native VM code. See the comment at the top of
+ * src/vm/ecall.cpp for instructions on how to add new overloads.
+ */
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char[] value);
@@ -335,8 +339,7 @@ namespace System
return Empty;
string result = FastAllocateString(value.Length);
- fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value))
- wstrcpy(dest, src, value.Length);
+ Buffer.Memmove(ref result._firstChar, ref MemoryMarshal.GetReference(value), (uint)value.Length);
return result;
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
index 2eb03d8089..d5782d58e4 100644
--- a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
@@ -278,7 +278,7 @@ namespace System.Text
Rune thisRune;
while ((thisRune = GetNextRune()).Value != 0)
{
- if (thisRune.TryEncode(chars, out int charsWrittenJustNow))
+ if (thisRune.TryEncodeToUtf16(chars, out int charsWrittenJustNow))
{
chars = chars.Slice(charsWrittenJustNow);
continue;
diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
index 597d362bf7..9040a94f0f 100644
--- a/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
@@ -332,7 +332,7 @@ namespace System.Text
switch (_encoding.DecodeFirstRune(combinedBuffer, out Rune value, out int combinedBufferBytesConsumed))
{
case OperationStatus.Done:
- if (value.TryEncode(chars, out charsWritten))
+ if (value.TryEncodeToUtf16(chars, out charsWritten))
{
goto Finish; // successfully transcoded bytes -> chars
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs b/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
index 09044afefe..0e32167957 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
@@ -327,7 +327,7 @@ namespace System.Text
// There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
// or (b) the encoding can't translate this scalar value.
- if (Rune.DecodeUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration) == OperationStatus.NeedMoreData
+ if (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration) == OperationStatus.NeedMoreData
&& encoder != null
&& !encoder.MustFlush)
{
@@ -603,7 +603,7 @@ namespace System.Text
// There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
// or (b) the encoding can't translate this scalar value.
- switch (Rune.DecodeUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration))
+ switch (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration))
{
case OperationStatus.NeedMoreData:
Debug.Assert(charsConsumedThisIteration == chars.Length, "If returning NeedMoreData, should out the entire buffer length as chars consumed.");
diff --git a/src/System.Private.CoreLib/shared/System/Text/Rune.cs b/src/System.Private.CoreLib/shared/System/Text/Rune.cs
index dc3c892ac6..a91c0fcb99 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Rune.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Rune.cs
@@ -231,20 +231,20 @@ namespace System.Text
/// </para>
/// </returns>
/// <remarks>
- /// The general calling convention is to call this method in a loop, slicing the <paramref name="utf16Source"/> buffer by
+ /// The general calling convention is to call this method in a loop, slicing the <paramref name="source"/> buffer by
/// <paramref name="charsConsumed"/> elements on each iteration of the loop. On each iteration of the loop <paramref name="result"/>
/// will contain the real scalar value if successfully decoded, or it will contain <see cref="ReplacementChar"/> if
/// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of
/// invalid sequences while iterating through the loop.
/// </remarks>
- public static OperationStatus DecodeUtf16(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
+ public static OperationStatus DecodeFromUtf16(ReadOnlySpan<char> source, out Rune result, out int charsConsumed)
{
- if (!utf16Source.IsEmpty)
+ if (!source.IsEmpty)
{
// First, check for the common case of a BMP scalar value.
// If this is correct, return immediately.
- char firstChar = utf16Source[0];
+ char firstChar = source[0];
if (TryCreate(firstChar, out result))
{
charsConsumed = 1;
@@ -255,9 +255,9 @@ namespace System.Text
// Let's optimistically assume for now it's a high surrogate and hope
// that combining it with the next char yields useful results.
- if (1 < (uint)utf16Source.Length)
+ if (1 < (uint)source.Length)
{
- char secondChar = utf16Source[1];
+ char secondChar = source[1];
if (TryCreate(firstChar, secondChar, out result))
{
// Success! Formed a supplementary scalar value.
@@ -284,7 +284,7 @@ namespace System.Text
// If we got to this point, the input buffer was empty, or the buffer
// was a single element in length and that element was a high surrogate char.
- charsConsumed = utf16Source.Length;
+ charsConsumed = source.Length;
result = ReplacementChar;
return OperationStatus.NeedMoreData;
@@ -296,66 +296,6 @@ namespace System.Text
}
/// <summary>
- /// Decodes the <see cref="Rune"/> at the end of the provided UTF-16 source buffer.
- /// </summary>
- /// <remarks>
- /// This method is very similar to <see cref="DecodeUtf16(ReadOnlySpan{char}, out Rune, out int)"/>, but it allows
- /// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
- /// of the loop, the caller should slice off the final <paramref name="charsConsumed"/> elements of
- /// the <paramref name="utf16Source"/> buffer.
- /// </remarks>
- public static OperationStatus DecodeUtf16FromEnd(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
- {
- int index = utf16Source.Length - 1;
- if ((uint)index < (uint)utf16Source.Length)
- {
- // First, check for the common case of a BMP scalar value.
- // If this is correct, return immediately.
-
- char finalChar = utf16Source[index];
- if (TryCreate(finalChar, out result))
- {
- charsConsumed = 1;
- return OperationStatus.Done;
- }
-
- if (char.IsLowSurrogate(finalChar))
- {
- // The final character was a UTF-16 low surrogate code point.
- // This must be preceded by a UTF-16 high surrogate code point, otherwise
- // we have a standalone low surrogate, which is always invalid.
-
- index--;
- if ((uint)index < (uint)utf16Source.Length)
- {
- char penultimateChar = utf16Source[index];
- if (TryCreate(penultimateChar, finalChar, out result))
- {
- // Success! Formed a supplementary scalar value.
- charsConsumed = 2;
- return OperationStatus.Done;
- }
- }
-
- // If we got to this point, we saw a standalone low surrogate
- // and must report an error.
-
- charsConsumed = 1; // standalone surrogate
- result = ReplacementChar;
- return OperationStatus.InvalidData;
- }
- }
-
- // If we got this far, the source buffer was empty, or the source buffer ended
- // with a UTF-16 high surrogate code point. These aren't errors since they could
- // be valid given more input data.
-
- charsConsumed = (int)((uint)(-utf16Source.Length) >> 31); // 0 -> 0, all other lengths -> 1
- result = ReplacementChar;
- return OperationStatus.NeedMoreData;
- }
-
- /// <summary>
/// Decodes the <see cref="Rune"/> at the beginning of the provided UTF-8 source buffer.
/// </summary>
/// <returns>
@@ -375,13 +315,13 @@ namespace System.Text
/// </para>
/// </returns>
/// <remarks>
- /// The general calling convention is to call this method in a loop, slicing the <paramref name="utf8Source"/> buffer by
+ /// The general calling convention is to call this method in a loop, slicing the <paramref name="source"/> buffer by
/// <paramref name="bytesConsumed"/> elements on each iteration of the loop. On each iteration of the loop <paramref name="result"/>
/// will contain the real scalar value if successfully decoded, or it will contain <see cref="ReplacementChar"/> if
/// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of
/// invalid sequences while iterating through the loop.
/// </remarks>
- public static OperationStatus DecodeUtf8(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ public static OperationStatus DecodeFromUtf8(ReadOnlySpan<byte> source, out Rune result, out int bytesConsumed)
{
// This method follows the Unicode Standard's recommendation for detecting
// the maximal subpart of an ill-formed subsequence. See The Unicode Standard,
@@ -393,12 +333,12 @@ namespace System.Text
// Try reading input[0].
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- uint tempValue = utf8Source[index];
+ uint tempValue = source[index];
if (!UnicodeUtility.IsAsciiCodePoint(tempValue))
{
goto NotAscii;
@@ -428,7 +368,7 @@ namespace System.Text
// Try reading input[1].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
@@ -437,7 +377,7 @@ namespace System.Text
// complement representation is in the range [-65..-128]. This allows us to
// perform a single comparison to see if a byte is a continuation byte.
- int thisByteSignExtended = (sbyte)utf8Source[index];
+ int thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid;
@@ -482,12 +422,12 @@ namespace System.Text
// Try reading input[2].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- thisByteSignExtended = (sbyte)utf8Source[index];
+ thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid; // this byte is not a UTF-8 continuation byte
@@ -507,12 +447,12 @@ namespace System.Text
// Try reading input[3].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- thisByteSignExtended = (sbyte)utf8Source[index];
+ thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid; // this byte is not a UTF-8 continuation byte
@@ -546,23 +486,83 @@ namespace System.Text
}
/// <summary>
+ /// Decodes the <see cref="Rune"/> at the end of the provided UTF-16 source buffer.
+ /// </summary>
+ /// <remarks>
+ /// This method is very similar to <see cref="DecodeFromUtf16(ReadOnlySpan{char}, out Rune, out int)"/>, but it allows
+ /// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
+ /// of the loop, the caller should slice off the final <paramref name="charsConsumed"/> elements of
+ /// the <paramref name="source"/> buffer.
+ /// </remarks>
+ public static OperationStatus DecodeLastFromUtf16(ReadOnlySpan<char> source, out Rune result, out int charsConsumed)
+ {
+ int index = source.Length - 1;
+ if ((uint)index < (uint)source.Length)
+ {
+ // First, check for the common case of a BMP scalar value.
+ // If this is correct, return immediately.
+
+ char finalChar = source[index];
+ if (TryCreate(finalChar, out result))
+ {
+ charsConsumed = 1;
+ return OperationStatus.Done;
+ }
+
+ if (char.IsLowSurrogate(finalChar))
+ {
+ // The final character was a UTF-16 low surrogate code point.
+ // This must be preceded by a UTF-16 high surrogate code point, otherwise
+ // we have a standalone low surrogate, which is always invalid.
+
+ index--;
+ if ((uint)index < (uint)source.Length)
+ {
+ char penultimateChar = source[index];
+ if (TryCreate(penultimateChar, finalChar, out result))
+ {
+ // Success! Formed a supplementary scalar value.
+ charsConsumed = 2;
+ return OperationStatus.Done;
+ }
+ }
+
+ // If we got to this point, we saw a standalone low surrogate
+ // and must report an error.
+
+ charsConsumed = 1; // standalone surrogate
+ result = ReplacementChar;
+ return OperationStatus.InvalidData;
+ }
+ }
+
+ // If we got this far, the source buffer was empty, or the source buffer ended
+ // with a UTF-16 high surrogate code point. These aren't errors since they could
+ // be valid given more input data.
+
+ charsConsumed = (int)((uint)(-source.Length) >> 31); // 0 -> 0, all other lengths -> 1
+ result = ReplacementChar;
+ return OperationStatus.NeedMoreData;
+ }
+
+ /// <summary>
/// Decodes the <see cref="Rune"/> at the end of the provided UTF-8 source buffer.
/// </summary>
/// <remarks>
- /// This method is very similar to <see cref="DecodeUtf8(ReadOnlySpan{byte}, out Rune, out int)"/>, but it allows
+ /// This method is very similar to <see cref="DecodeFromUtf8(ReadOnlySpan{byte}, out Rune, out int)"/>, but it allows
/// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
/// of the loop, the caller should slice off the final <paramref name="bytesConsumed"/> elements of
- /// the <paramref name="utf8Source"/> buffer.
+ /// the <paramref name="source"/> buffer.
/// </remarks>
- public static OperationStatus DecodeUtf8FromEnd(ReadOnlySpan<byte> utf8Source, out Rune value, out int bytesConsumed)
+ public static OperationStatus DecodeLastFromUtf8(ReadOnlySpan<byte> source, out Rune value, out int bytesConsumed)
{
- int index = utf8Source.Length - 1;
- if ((uint)index < (uint)utf8Source.Length)
+ int index = source.Length - 1;
+ if ((uint)index < (uint)source.Length)
{
// The buffer contains at least one byte. Let's check the fast case where the
// buffer ends with an ASCII byte.
- uint tempValue = utf8Source[index];
+ uint tempValue = source[index];
if (UnicodeUtility.IsAsciiCodePoint(tempValue))
{
bytesConsumed = 1;
@@ -582,7 +582,7 @@ namespace System.Text
// This is a UTF-8 leading byte. We'll do a forward read from here.
// It'll return invalid (if given C0, F5, etc.) or incomplete. Both are fine.
- return DecodeUtf8(utf8Source.Slice(index), out value, out bytesConsumed);
+ return DecodeFromUtf8(source.Slice(index), out value, out bytesConsumed);
}
// If we got to this point, the final byte was a UTF-8 continuation byte.
@@ -591,7 +591,7 @@ namespace System.Text
for (int i = 3; i > 0; i--)
{
index--;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto Invalid; // out of data
}
@@ -600,7 +600,7 @@ namespace System.Text
// (bits 0xC0 set, values C0..FF). In two's complement this is the range [-64..127].
// It's just a fast way for us to terminate the search.
- if ((sbyte)utf8Source[index] >= -64)
+ if ((sbyte)source[index] >= -64)
{
goto ForwardDecode;
}
@@ -628,11 +628,11 @@ namespace System.Text
// fine since it'll be handled by the forward read. From this position, we'll perform a forward read
// and see if we consumed the entirety of the buffer.
- utf8Source = utf8Source.Slice(index);
- Debug.Assert(!utf8Source.IsEmpty, "Shouldn't reach this for empty inputs.");
+ source = source.Slice(index);
+ Debug.Assert(!source.IsEmpty, "Shouldn't reach this for empty inputs.");
- OperationStatus operationStatus = DecodeUtf8(utf8Source, out Rune tempRune, out int tempBytesConsumed);
- if (tempBytesConsumed == utf8Source.Length)
+ OperationStatus operationStatus = DecodeFromUtf8(source, out Rune tempRune, out int tempBytesConsumed);
+ if (tempBytesConsumed == source.Length)
{
// If this forward read consumed the entirety of the end of the input buffer, we can return it
// as the result of this function. It could be well-formed, incomplete, or invalid. If it's
@@ -662,15 +662,82 @@ namespace System.Text
}
}
- // returns the number of chars written
- private int EncodeToUtf16(Span<char> destination)
+ public static OperationStatus DecodeUtf16(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
{
- Debug.Assert(destination.Length >= Utf16SequenceLength, "Caller should've provided a large enough buffer.");
- bool success = TryEncode(destination, out int charsWritten);
- Debug.Assert(success, "TryEncode should never fail given a large enough buffer.");
+ // [TODO] This method was renamed to DecodeFromUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeFromUtf16(utf16Source, out result, out charsConsumed);
+ }
+
+ public static OperationStatus DecodeUtf16FromEnd(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
+ {
+ // [TODO] This method was renamed to DecodeLastFromUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeLastFromUtf16(utf16Source, out result, out charsConsumed);
+ }
+
+ public static OperationStatus DecodeUtf8(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ {
+ // [TODO] This method was renamed to DecodeFromUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeFromUtf8(utf8Source, out result, out bytesConsumed);
+ }
+
+ public static OperationStatus DecodeUtf8FromEnd(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ {
+ // [TODO] This method was renamed to DecodeLastFromUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeLastFromUtf8(utf8Source, out result, out bytesConsumed);
+ }
+
+ /// <summary>
+ /// Encodes this <see cref="Rune"/> to a UTF-16 destination buffer.
+ /// </summary>
+ /// <param name="destination">The buffer to which to write this value as UTF-16.</param>
+ /// <returns>The number of <see cref="char"/>s written to <paramref name="destination"/>.</returns>
+ /// <exception cref="ArgumentException">
+ /// If <paramref name="destination"/> is not large enough to hold the output.
+ /// </exception>
+ public int EncodeToUtf16(Span<char> destination)
+ {
+ if (!TryEncodeToUtf16(destination, out int charsWritten))
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+
return charsWritten;
}
+ /// <summary>
+ /// Encodes this <see cref="Rune"/> to a UTF-8 destination buffer.
+ /// </summary>
+ /// <param name="destination">The buffer to which to write this value as UTF-8.</param>
+ /// <returns>The number of <see cref="byte"/>s written to <paramref name="destination"/>.</returns>
+ /// <exception cref="ArgumentException">
+ /// If <paramref name="destination"/> is not large enough to hold the output.
+ /// </exception>
+ public int EncodeToUtf8(Span<byte> destination)
+ {
+ if (!TryEncodeToUtf8(destination, out int bytesWritten))
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+
+ return bytesWritten;
+ }
+
public override bool Equals(object obj) => (obj is Rune other) && this.Equals(other);
public bool Equals(Rune other) => (this == other);
@@ -893,7 +960,7 @@ namespace System.Text
/// The <see cref="Utf16SequenceLength"/> property can be queried ahead of time to determine
/// the required size of the <paramref name="destination"/> buffer.
/// </remarks>
- public bool TryEncode(Span<char> destination, out int charsWritten)
+ public bool TryEncodeToUtf16(Span<char> destination, out int charsWritten)
{
if (destination.Length >= 1)
{
@@ -917,6 +984,16 @@ namespace System.Text
return false;
}
+ public bool TryEncode(Span<char> destination, out int charsWritten)
+ {
+ // [TODO] This method was renamed to TryEncodeToUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return TryEncodeToUtf16(destination, out charsWritten);
+ }
+
/// <summary>
/// Encodes this <see cref="Rune"/> to a destination buffer as UTF-8 bytes.
/// </summary>
@@ -929,10 +1006,8 @@ namespace System.Text
/// The <see cref="Utf8SequenceLength"/> property can be queried ahead of time to determine
/// the required size of the <paramref name="destination"/> buffer.
/// </remarks>
- public bool TryEncodeToUtf8Bytes(Span<byte> destination, out int bytesWritten)
+ public bool TryEncodeToUtf8(Span<byte> destination, out int bytesWritten)
{
- // TODO: Optimize some of these writes by using BMI2 instructions.
-
// The bit patterns below come from the Unicode Standard, Table 3-6.
if (destination.Length >= 1)
@@ -987,6 +1062,16 @@ namespace System.Text
return false;
}
+ public bool TryEncodeToUtf8Bytes(Span<byte> destination, out int bytesWritten)
+ {
+ // [TODO] This method was renamed to TryEncodeToUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return TryEncodeToUtf8(destination, out bytesWritten);
+ }
+
/// <summary>
/// Attempts to get the <see cref="Rune"/> which begins at index <paramref name="index"/> in
/// string <paramref name="input"/>.
diff --git a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
index 0b941920af..9e6f245c10 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
@@ -48,7 +48,7 @@ namespace System.Text.Unicode
while (!source.IsEmpty)
{
- status = Rune.DecodeUtf16(source, out Rune firstScalarValue, out int charsConsumed);
+ status = Rune.DecodeFromUtf16(source, out Rune firstScalarValue, out int charsConsumed);
switch (status)
{
@@ -86,7 +86,7 @@ namespace System.Text.Unicode
// Do so now, and only terminate the loop if we ran out of space
// in the destination buffer.
- if (firstScalarValue.TryEncodeToUtf8Bytes(destination, out int bytesWritten))
+ if (firstScalarValue.TryEncodeToUtf8(destination, out int bytesWritten))
{
source = source.Slice(charsConsumed); // don't use Rune.Utf8SequenceLength; we may have performed substitution
destination = destination.Slice(bytesWritten);
@@ -131,7 +131,7 @@ namespace System.Text.Unicode
while (!source.IsEmpty)
{
- status = Rune.DecodeUtf8(source, out Rune firstScalarValue, out int bytesConsumed);
+ status = Rune.DecodeFromUtf8(source, out Rune firstScalarValue, out int bytesConsumed);
switch (status)
{
@@ -169,7 +169,7 @@ namespace System.Text.Unicode
// Do so now, and only terminate the loop if we ran out of space
// in the destination buffer.
- if (firstScalarValue.TryEncode(destination, out int charsWritten))
+ if (firstScalarValue.TryEncodeToUtf16(destination, out int charsWritten))
{
source = source.Slice(bytesConsumed); // don't use Rune.Utf16SequenceLength; we may have performed substitution
destination = destination.Slice(charsWritten);
diff --git a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs
new file mode 100644
index 0000000000..6ee9ca05a6
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs
@@ -0,0 +1,106 @@
+// 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.IO;
+using System.Runtime.CompilerServices;
+
+namespace System.Text.Unicode
+{
+ internal static class Utf8Utility
+ {
+ /// <summary>
+ /// The maximum number of bytes that can result from UTF-8 transcoding
+ /// any Unicode scalar value.
+ /// </summary>
+ internal const int MaxBytesPerScalar = 4;
+
+ /// <summary>
+ /// The UTF-8 representation of <see cref="UnicodeUtility.ReplacementChar"/>.
+ /// </summary>
+ private static ReadOnlySpan<byte> ReplacementCharSequence => new byte[] { 0xEF, 0xBF, 0xBD };
+
+ /// <summary>
+ /// Returns the byte index in <paramref name="utf8Data"/> where the first invalid UTF-8 sequence begins,
+ /// or -1 if the buffer contains no invalid sequences. Also outs the <paramref name="isAscii"/> parameter
+ /// stating whether all data observed (up to the first invalid sequence or the end of the buffer, whichever
+ /// comes first) is ASCII.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetIndexOfFirstInvalidUtf8Sequence(ReadOnlySpan<byte> utf8Data, out bool isAscii)
+ {
+ // TODO_UTF8STRING: Replace this with the faster drop-in replacement when it's available (coreclr #21948).
+
+ bool tempIsAscii = true;
+ int originalDataLength = utf8Data.Length;
+
+ while (!utf8Data.IsEmpty)
+ {
+ if (Rune.DecodeFromUtf8(utf8Data, out Rune result, out int bytesConsumed) != OperationStatus.Done)
+ {
+ break;
+ }
+
+ tempIsAscii &= result.IsAscii;
+ utf8Data = utf8Data.Slice(bytesConsumed);
+ }
+
+ isAscii = tempIsAscii;
+ return (utf8Data.IsEmpty) ? -1 : (originalDataLength - utf8Data.Length);
+ }
+
+#if FEATURE_UTF8STRING
+ /// <summary>
+ /// Returns <paramref name="value"/> if it is null or contains only well-formed UTF-8 data;
+ /// otherwises allocates a new <see cref="Utf8String"/> instance containing the same data as
+ /// <paramref name="value"/> but where all invalid UTF-8 sequences have been replaced
+ /// with U+FFD.
+ /// </summary>
+ public static Utf8String ValidateAndFixupUtf8String(Utf8String value)
+ {
+ if (Utf8String.IsNullOrEmpty(value))
+ {
+ return value;
+ }
+
+ ReadOnlySpan<byte> valueAsBytes = value.AsBytes();
+
+ int idxOfFirstInvalidData = GetIndexOfFirstInvalidUtf8Sequence(valueAsBytes, out _);
+ if (idxOfFirstInvalidData < 0)
+ {
+ return value;
+ }
+
+ // TODO_UTF8STRING: Replace this with the faster implementation once it's available.
+ // (The faster implementation is in the dev/utf8string_bak branch currently.)
+
+ MemoryStream memStream = new MemoryStream();
+ memStream.Write(valueAsBytes.Slice(0, idxOfFirstInvalidData));
+
+ valueAsBytes = valueAsBytes.Slice(idxOfFirstInvalidData);
+ do
+ {
+ if (Rune.DecodeFromUtf8(valueAsBytes, out _, out int bytesConsumed) == OperationStatus.Done)
+ {
+ // Valid scalar value - copy data as-is to MemoryStream
+ memStream.Write(valueAsBytes.Slice(0, bytesConsumed));
+ }
+ else
+ {
+ // Invalid scalar value - copy U+FFFD to MemoryStream
+ memStream.Write(ReplacementCharSequence);
+ }
+
+ valueAsBytes = valueAsBytes.Slice(bytesConsumed);
+ } while (!valueAsBytes.IsEmpty);
+
+ bool success = memStream.TryGetBuffer(out ArraySegment<byte> memStreamBuffer);
+ Debug.Assert(success, "Couldn't get underlying MemoryStream buffer.");
+
+ return Utf8String.DangerousCreateWithoutValidation(memStreamBuffer, assumeWellFormed: true);
+ }
+#endif // FEATURE_UTF8STRING
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs b/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
index 3aad29679d..065c938d81 100644
--- a/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
@@ -11,7 +11,7 @@ namespace System.Text
/// <summary>
/// The Unicode replacement character U+FFFD.
/// </summary>
- public const uint ReplacementChar = 0xFFFDU;
+ public const uint ReplacementChar = 0xFFFD;
/// <summary>
/// Returns the Unicode plane (0 through 16, inclusive) which contains this code point.
diff --git a/src/System.Private.CoreLib/shared/System/ThrowHelper.cs b/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
index 06b3ce41a6..4185382497 100644
--- a/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
+++ b/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
@@ -68,7 +68,7 @@ namespace System
internal static void ThrowArgumentException_DestinationTooShort()
{
- throw new ArgumentException(SR.Argument_DestinationTooShort);
+ throw new ArgumentException(SR.Argument_DestinationTooShort, "destination");
}
internal static void ThrowArgumentException_OverlapAlignmentMismatch()
diff --git a/src/System.Private.CoreLib/src/System/Char8.cs b/src/System.Private.CoreLib/src/System/Char8.cs
new file mode 100644
index 0000000000..7a71e2faa0
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Char8.cs
@@ -0,0 +1,69 @@
+// 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
+{
+ /// <summary>
+ /// Represents a UTF-8 code unit, the elemental type of <see cref="Utf8String"/>.
+ /// </summary>
+ public readonly struct Char8 : IComparable<Char8>, IEquatable<Char8>
+ {
+ private readonly byte _value;
+
+ private Char8(byte value)
+ {
+ _value = value;
+ }
+
+ public static bool operator ==(Char8 left, Char8 right) => left._value == right._value;
+ public static bool operator !=(Char8 left, Char8 right) => left._value != right._value;
+ public static bool operator <(Char8 left, Char8 right) => left._value < right._value;
+ public static bool operator <=(Char8 left, Char8 right) => left._value <= right._value;
+ public static bool operator >(Char8 left, Char8 right) => left._value > right._value;
+ public static bool operator >=(Char8 left, Char8 right) => left._value >= right._value;
+
+ // Operators from Utf8Char to <other primitives>
+ // TODO: Once C# gets support for checked operators, we should add those here.
+
+ public static implicit operator byte(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static explicit operator sbyte(Char8 value) => (sbyte)value._value; // explicit because can integer overflow
+ public static explicit operator char(Char8 value) => (char)value._value; // explicit because don't want to encourage char conversion
+ public static implicit operator short(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator ushort(Char8 value) => value._value;
+ public static implicit operator int(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator uint(Char8 value) => value._value;
+ public static implicit operator long(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator ulong(Char8 value) => value._value;
+
+ // Operators from <other primitives> to Char8; most are explicit because narrowing conversions could be lossy
+ // TODO: Once C# gets support for checked operators, we should add those here.
+
+ public static implicit operator Char8(byte value) => new Char8(value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(sbyte value) => new Char8((byte)value);
+ public static explicit operator Char8(char value) => new Char8((byte)value);
+ public static explicit operator Char8(short value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(ushort value) => new Char8((byte)value);
+ public static explicit operator Char8(int value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(uint value) => new Char8((byte)value);
+ public static explicit operator Char8(long value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(ulong value) => new Char8((byte)value);
+
+ public int CompareTo(Char8 other) => this._value.CompareTo(other._value);
+
+ public override bool Equals(object obj) => (obj is Char8 other) && (this == other);
+ public bool Equals(Char8 other) => this == other;
+
+ public override int GetHashCode() => _value;
+
+ public override string ToString() => _value.ToString("X2");
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
index 1a5de88bd1..9f05540610 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
@@ -196,9 +196,9 @@ namespace System.Diagnostics.Tracing
s_sessionID = EventPipeInternal.Enable(
configuration.OutputFile,
configuration.CircularBufferSizeInMB,
- configuration.ProfilerSamplingRateInNanoseconds,
+ (ulong)configuration.ProfilerSamplingRateInNanoseconds,
providers,
- providers.Length,
+ (uint)providers.Length,
configuration.MultiFileTraceLengthInSeconds);
}
@@ -214,7 +214,13 @@ namespace System.Diagnostics.Tracing
// These PInvokes are used by the configuration APIs to interact with EventPipe.
//
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- internal static extern UInt64 Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders, ulong multiFileTraceLengthInSeconds);
+ internal static extern UInt64 Enable(
+ string outputFile,
+ uint circularBufferSizeInMB,
+ ulong profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration[] providers,
+ uint numProviders,
+ ulong multiFileTraceLengthInSeconds);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern void Disable(UInt64 sessionID);
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
index 046d5f0dac..010c2ea629 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
@@ -15,17 +15,11 @@ namespace System.Diagnostics.Tracing
internal sealed class RuntimeEventSource : EventSource
{
private static RuntimeEventSource s_RuntimeEventSource;
- private EventCounter[] _counters;
-
- private enum Counter {
- GCHeapSize,
- Gen0GCCount,
- Gen1GCCount,
- Gen2GCCount,
- ExceptionCount
- }
-
- private Timer _timer;
+ private PollingCounter _gcHeapSizeCounter;
+ private IncrementingPollingCounter _gen0GCCounter;
+ private IncrementingPollingCounter _gen1GCCounter;
+ private IncrementingPollingCounter _gen2GCCounter;
+ private IncrementingPollingCounter _exceptionCounter;
private const int EnabledPollingIntervalMilliseconds = 1000; // 1 second
@@ -36,84 +30,23 @@ namespace System.Diagnostics.Tracing
private RuntimeEventSource(): base(new Guid(0x49592C0F, 0x5A05, 0x516D, 0xAA, 0x4B, 0xA6, 0x4E, 0x02, 0x02, 0x6C, 0x89), "System.Runtime", EventSourceSettings.EtwSelfDescribingEventFormat)
{
-
}
protected override void OnEventCommand(System.Diagnostics.Tracing.EventCommandEventArgs command)
{
if (command.Command == EventCommand.Enable)
{
- if (_counters == null)
- {
- // NOTE: These counters will NOT be disposed on disable command because we may be introducing
- // a race condition by doing that. We still want to create these lazily so that we aren't adding
- // overhead by at all times even when counters aren't enabled.
- _counters = new EventCounter[] {
- // TODO: process info counters
-
- // GC info counters
- new EventCounter("Total Memory by GC", this),
- new EventCounter("Gen 0 GC Count", this),
- new EventCounter("Gen 1 GC Count", this),
- new EventCounter("Gen 2 GC Count", this),
-
- new EventCounter("Exception Count", this)
- };
- }
-
-
- // Initialize the timer, but don't set it to run.
- // The timer will be set to run each time PollForTracingCommand is called.
-
- // TODO: We should not need this timer once we are done settling upon a high-level design for
- // what EventCounter is capable of doing. Once that decision is made, we should be able to
- // get rid of this.
- if (_timer == null)
- {
- _timer = new Timer(
- callback: new TimerCallback(PollForCounterUpdate),
- state: null,
- dueTime: Timeout.Infinite,
- period: Timeout.Infinite,
- flowExecutionContext: false);
- }
- // Trigger the first poll operation on when this EventSource is enabled
- PollForCounterUpdate(null);
- }
- else if (command.Command == EventCommand.Disable)
- {
- if (_timer != null)
- {
- _timer.Change(Timeout.Infinite, Timeout.Infinite); // disable the timer from running until System.Runtime is re-enabled
- }
- }
- }
-
- public void UpdateAllCounters()
- {
- // GC counters
- _counters[(int)Counter.GCHeapSize].WriteMetric(GC.GetTotalMemory(false));
- _counters[(int)Counter.Gen0GCCount].WriteMetric(GC.CollectionCount(0));
- _counters[(int)Counter.Gen1GCCount].WriteMetric(GC.CollectionCount(1));
- _counters[(int)Counter.Gen2GCCount].WriteMetric(GC.CollectionCount(2));
- _counters[(int)Counter.ExceptionCount].WriteMetric(Exception.GetExceptionCount());
- }
-
- private void PollForCounterUpdate(object state)
- {
- // TODO: Need to confirm with vancem about how to do error-handling here.
- // This disables to timer from getting rescheduled to run, which may or may not be
- // what we eventually want.
-
- // Make sure that any transient errors don't cause the listener thread to exit.
- try
- {
- UpdateAllCounters();
-
- // Schedule the timer to run again.
- _timer.Change(EnabledPollingIntervalMilliseconds, Timeout.Infinite);
+ // NOTE: These counters will NOT be disposed on disable command because we may be introducing
+ // a race condition by doing that. We still want to create these lazily so that we aren't adding
+ // overhead by at all times even when counters aren't enabled.
+
+ // On disable, PollingCounters will stop polling for values so it should be fine to leave them around.
+ _gcHeapSizeCounter = _gcHeapSizeCounter ?? new PollingCounter("GC Heap Size", this, () => GC.GetTotalMemory(false));
+ _gen0GCCounter = _gen0GCCounter ?? new IncrementingPollingCounter("Gen 0 GC Count", this, () => GC.CollectionCount(0));
+ _gen1GCCounter = _gen1GCCounter ?? new IncrementingPollingCounter("Gen 1 GC Count", this, () => GC.CollectionCount(1));
+ _gen2GCCounter = _gen2GCCounter ?? new IncrementingPollingCounter("Gen 2 GC Count", this, () => GC.CollectionCount(2));
+ _exceptionCounter = _exceptionCounter ?? new IncrementingPollingCounter("Exception Count", this, () => Exception.GetExceptionCount());
}
- catch { }
}
}
}
diff --git a/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
new file mode 100644
index 0000000000..1c4e4d1648
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
@@ -0,0 +1,113 @@
+// 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;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ public abstract partial class Enum
+ {
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern void GetEnumValuesAndNames(RuntimeTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, bool getNames);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public override extern bool Equals(object obj);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object InternalBoxEnum(RuntimeType enumType, long value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern int InternalCompareTo(object o1, object o2);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern CorElementType InternalGetCorElementType();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern bool InternalHasFlag(Enum flags);
+
+ private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
+ {
+ TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
+
+ if (entry == null || (getNames && entry.Names == null))
+ {
+ ulong[] values = null;
+ string[] names = null;
+ GetEnumValuesAndNames(
+ enumType.GetTypeHandleInternal(),
+ JitHelpers.GetObjectHandleOnStack(ref values),
+ JitHelpers.GetObjectHandleOnStack(ref names),
+ getNames);
+ bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
+
+ entry = new TypeValuesAndNames(isFlags, values, names);
+ enumType.GenericCache = entry;
+ }
+
+ return entry;
+ }
+
+ [Intrinsic]
+ public bool HasFlag(Enum flag)
+ {
+ if (flag == null)
+ throw new ArgumentNullException(nameof(flag));
+
+ if (!this.GetType().IsEquivalentTo(flag.GetType()))
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), this.GetType()));
+ }
+
+ return InternalHasFlag(flag);
+ }
+
+ public static string GetName(Type enumType, object value)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+
+ return enumType.GetEnumName(value);
+ }
+
+ public static string[] GetNames(Type enumType)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+
+ return enumType.GetEnumNames();
+ }
+
+ public static Type GetUnderlyingType(Type enumType)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+
+ return enumType.GetEnumUnderlyingType();
+ }
+
+ public static Array GetValues(Type enumType)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+
+ return enumType.GetEnumValues();
+ }
+
+ private static RuntimeType ValidateRuntimeType(Type enumType)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+ if (!enumType.IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
+ if (!(enumType is RuntimeType rtType))
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
+ return rtType;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs
index 7ed70e4cc2..c4e36b937f 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs
@@ -55,12 +55,12 @@ namespace System.Reflection
}
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern void GetExecutingAssembly(StackCrawlMarkHandle stackMark, ObjectHandleOnStack retAssembly);
+ private static extern void GetExecutingAssemblyNative(StackCrawlMarkHandle stackMark, ObjectHandleOnStack retAssembly);
internal static RuntimeAssembly GetExecutingAssembly(ref StackCrawlMark stackMark)
{
RuntimeAssembly retAssembly = null;
- GetExecutingAssembly(JitHelpers.GetStackCrawlMarkHandle(ref stackMark), JitHelpers.GetObjectHandleOnStack(ref retAssembly));
+ GetExecutingAssemblyNative(JitHelpers.GetStackCrawlMarkHandle(ref stackMark), JitHelpers.GetObjectHandleOnStack(ref retAssembly));
return retAssembly;
}
@@ -83,7 +83,7 @@ namespace System.Reflection
}
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern void GetEntryAssembly(ObjectHandleOnStack retAssembly);
+ private static extern void GetEntryAssemblyNative(ObjectHandleOnStack retAssembly);
// internal test hook
private static bool s_forceNullEntryPoint = false;
@@ -94,7 +94,7 @@ namespace System.Reflection
return null;
RuntimeAssembly entryAssembly = null;
- GetEntryAssembly(JitHelpers.GetObjectHandleOnStack(ref entryAssembly));
+ GetEntryAssemblyNative(JitHelpers.GetObjectHandleOnStack(ref entryAssembly));
return entryAssembly;
}
diff --git a/src/System.Private.CoreLib/src/System/Reflection/MdImport.cs b/src/System.Private.CoreLib/src/System/Reflection/MdImport.cs
index 3277bb0210..e729d3cc17 100644
--- a/src/System.Private.CoreLib/src/System/Reflection/MdImport.cs
+++ b/src/System.Private.CoreLib/src/System/Reflection/MdImport.cs
@@ -10,46 +10,6 @@ using System.Runtime.InteropServices;
namespace System.Reflection
{
- internal enum CorElementType : byte
- {
- ELEMENT_TYPE_END = 0x00,
- ELEMENT_TYPE_VOID = 0x01,
- ELEMENT_TYPE_BOOLEAN = 0x02,
- ELEMENT_TYPE_CHAR = 0x03,
- ELEMENT_TYPE_I1 = 0x04,
- ELEMENT_TYPE_U1 = 0x05,
- ELEMENT_TYPE_I2 = 0x06,
- ELEMENT_TYPE_U2 = 0x07,
- ELEMENT_TYPE_I4 = 0x08,
- ELEMENT_TYPE_U4 = 0x09,
- ELEMENT_TYPE_I8 = 0x0A,
- ELEMENT_TYPE_U8 = 0x0B,
- ELEMENT_TYPE_R4 = 0x0C,
- ELEMENT_TYPE_R8 = 0x0D,
- ELEMENT_TYPE_STRING = 0x0E,
- ELEMENT_TYPE_PTR = 0x0F,
- ELEMENT_TYPE_BYREF = 0x10,
- ELEMENT_TYPE_VALUETYPE = 0x11,
- ELEMENT_TYPE_CLASS = 0x12,
- ELEMENT_TYPE_VAR = 0x13,
- ELEMENT_TYPE_ARRAY = 0x14,
- ELEMENT_TYPE_GENERICINST = 0x15,
- ELEMENT_TYPE_TYPEDBYREF = 0x16,
- ELEMENT_TYPE_I = 0x18,
- ELEMENT_TYPE_U = 0x19,
- ELEMENT_TYPE_FNPTR = 0x1B,
- ELEMENT_TYPE_OBJECT = 0x1C,
- ELEMENT_TYPE_SZARRAY = 0x1D,
- ELEMENT_TYPE_MVAR = 0x1E,
- ELEMENT_TYPE_CMOD_REQD = 0x1F,
- ELEMENT_TYPE_CMOD_OPT = 0x20,
- ELEMENT_TYPE_INTERNAL = 0x21,
- ELEMENT_TYPE_MAX = 0x22,
- ELEMENT_TYPE_MODIFIER = 0x40,
- ELEMENT_TYPE_SENTINEL = 0x41,
- ELEMENT_TYPE_PINNED = 0x45,
- }
-
[Flags()]
internal enum MdSigCallingConvention : byte
{
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 1851d9fb2b..74bb384486 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -147,7 +147,18 @@ namespace System.Runtime.CompilerServices
public static bool IsReferenceOrContainsReferences<T>()
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForRuntimeHelpers for how this happens.
+ throw new InvalidOperationException();
+ }
+
+ /// <returns>true if given type is bitwise equatable (memcmp can be used for equality checking)</returns>
+ /// <remarks>
+ /// Only use the result of this for Equals() comparison, not for CompareTo() comparison.
+ /// </remarks>
+ internal static bool IsBitwiseEquatable<T>()
+ {
+ // The body of this function will be replaced by the EE with unsafe code!!!
+ // See getILIntrinsicImplementationForRuntimeHelpers for how this happens.
throw new InvalidOperationException();
}
diff --git a/src/System.Private.CoreLib/src/System/Utf8Extensions.cs b/src/System.Private.CoreLib/src/System/Utf8Extensions.cs
new file mode 100644
index 0000000000..9fa2a54f16
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8Extensions.cs
@@ -0,0 +1,367 @@
+// 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.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ public static class Utf8Extensions
+ {
+ /// <summary>
+ /// Projects <paramref name="text"/> as a <see cref="ReadOnlySpan{Byte}"/>.
+ /// </summary>
+ public static ReadOnlySpan<byte> AsBytes(this ReadOnlySpan<Char8> text)
+ {
+ return MemoryMarshal.Cast<Char8, byte>(text);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlySpan<byte>(ref text.DangerousGetMutableReference(), text.Length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String 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<byte>(ref text.DangerousGetMutableReference(start), text.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlySpan<byte>(ref text.DangerousGetMutableReference(start), length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlySpan<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference()), text.Length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String 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<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference(start)), text.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlySpan<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference(start)), length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlyMemory<Char8>(text, 0, text.Length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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<Char8> AsMemory(this Utf8String 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<Char8>(text, start, text.Length - start);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, Index startIndex)
+ {
+ if (text == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(text.Length);
+ if ((uint)actualIndex > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlyMemory<Char8>(text, actualIndex, text.Length - actualIndex);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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<Char8> AsMemory(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlyMemory<Char8>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, Range range)
+ {
+ if (text == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(text.Length);
+ return new ReadOnlyMemory<Char8>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlyMemory<byte>(text, 0, text.Length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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<byte> AsMemoryBytes(this Utf8String 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<byte>(text, start, text.Length - start);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, Index startIndex)
+ {
+ if (text == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(text.Length);
+ if ((uint)actualIndex > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlyMemory<byte>(text, actualIndex, text.Length - actualIndex);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</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<byte> AsMemoryBytes(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlyMemory<byte>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, Range range)
+ {
+ if (text == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(text.Length);
+ return new ReadOnlyMemory<byte>(text, start, length);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs b/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs
new file mode 100644
index 0000000000..9ecd44f3ae
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs
@@ -0,0 +1,223 @@
+// 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.Runtime.InteropServices;
+using System.Text;
+using System.Text.Unicode;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ /*
+ * CONSTRUCTORS
+ *
+ * Defining a new constructor for string-like types (like Utf8String) requires changes both
+ * to the managed code below and to the native VM code. See the comment at the top of
+ * src/vm/ecall.cpp for instructions on how to add new overloads.
+ *
+ * The default behavior of each ctor is to validate the input, replacing invalid sequences with the
+ * Unicode replacement character U+FFFD. The resulting Utf8String instance will be well-formed but
+ * might not have full fidelity with the input data. This behavior can be controlled by calling
+ * any of the Create instances and specifying a different action.
+ */
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(ReadOnlySpan<byte> value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(ReadOnlySpan<byte> value)
+ {
+ if (value.IsEmpty)
+ {
+ return Empty;
+ }
+
+ Utf8String newString = FastAllocate(value.Length);
+ Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref MemoryMarshal.GetReference(value), (uint)value.Length);
+ return Utf8Utility.ValidateAndFixupUtf8String(newString);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(byte[] value, int startIndex, int length);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(byte[] value, int startIndex, int length) => Ctor(new ReadOnlySpan<byte>(value, startIndex, length));
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [CLSCompliant(false)]
+ public unsafe extern Utf8String(byte* value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private unsafe Utf8String Ctor(byte* value)
+ {
+ if (value == null)
+ {
+ return Empty;
+ }
+
+ return Ctor(new ReadOnlySpan<byte>(value, string.strlen(value)));
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(ReadOnlySpan<char> value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(ReadOnlySpan<char> value)
+ {
+ if (value.IsEmpty)
+ {
+ return Empty;
+ }
+
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+
+ Utf8String newString = FastAllocate(Encoding.UTF8.GetByteCount(value));
+ Encoding.UTF8.GetBytes(value, new Span<byte>(ref newString.DangerousGetMutableReference(), newString.Length));
+ return newString;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(char[] value, int startIndex, int length);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(char[] value, int startIndex, int length) => Ctor(new ReadOnlySpan<char>(value, startIndex, length));
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [CLSCompliant(false)]
+ public unsafe extern Utf8String(char* value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private unsafe Utf8String Ctor(char* value)
+ {
+ if (value == null)
+ {
+ return Empty;
+ }
+
+ return Ctor(new ReadOnlySpan<char>(value, string.wcslen(value)));
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(string value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(string value) => Ctor(value.AsSpan());
+
+ /*
+ * HELPER METHODS
+ */
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing data, bypassing validation.
+ /// Also allows the caller to set flags dictating various attributes of the data.
+ /// </summary>
+ internal static Utf8String DangerousCreateWithoutValidation(ReadOnlySpan<byte> utf8Data, bool assumeWellFormed = false, bool assumeAscii = false)
+ {
+ if (utf8Data.IsEmpty)
+ {
+ return Empty;
+ }
+
+ Utf8String newString = FastAllocate(utf8Data.Length);
+ utf8Data.CopyTo(new Span<byte>(ref newString.DangerousGetMutableReference(), newString.Length));
+ return newString;
+ }
+
+ /// <summary>
+ /// Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes
+ /// because instances are null-terminated.
+ /// </summary>
+ /// <remarks>
+ /// The implementation of this method checks its input argument for overflow.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern Utf8String FastAllocate(int length);
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs b/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs
new file mode 100644
index 0000000000..6e5209962f
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs
@@ -0,0 +1,109 @@
+// 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;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ /// <summary>
+ /// Substrings this <see cref="Utf8String"/> without bounds checking.
+ /// </summary>
+ private Utf8String InternalSubstring(int startIndex, int length)
+ {
+ Debug.Assert(startIndex >= 0, "StartIndex cannot be negative.");
+ Debug.Assert(startIndex <= this.Length, "StartIndex cannot point beyond the end of the string (except to the null terminator).");
+ Debug.Assert(length >= 0, "Length cannot be negative.");
+ Debug.Assert(startIndex + length <= this.Length, "StartIndex and Length cannot point beyond the end of the string.");
+
+ Debug.Assert(startIndex != 0 && startIndex != this.Length, "Caller should handle StartIndex boundary conditions.");
+ Debug.Assert(length != 0 && length != this.Length, "Caller should handle Length boundary conditions.");
+
+ Utf8String newString = FastAllocate(length);
+ Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref this.DangerousGetMutableReference(startIndex), (uint)length);
+ return newString;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Utf8String Substring(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(Length);
+ return Substring(actualIndex);
+ }
+
+ public Utf8String Substring(int startIndex)
+ {
+ if ((uint)startIndex > (uint)this.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex);
+ }
+
+ // Optimizations: since instances are immutable, we can return 'this' or the known
+ // Empty instance if the caller passed us a startIndex at the string boundary.
+
+ if (startIndex == 0)
+ {
+ return this;
+ }
+
+ if (startIndex == Length)
+ {
+ return Empty;
+ }
+
+ return InternalSubstring(startIndex, Length - startIndex);
+ }
+
+ public Utf8String Substring(int startIndex, int length)
+ {
+ ValidateStartIndexAndLength(startIndex, length);
+
+ // Optimizations: since instances are immutable, we can return 'this' or the known
+ // Empty instance if the caller passed us a startIndex at the string boundary.
+
+ if (length == 0)
+ {
+ return Empty;
+ }
+
+ if (length == this.Length)
+ {
+ return this;
+ }
+
+ return InternalSubstring(startIndex, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Utf8String Substring(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(Length);
+ return Substring(start, length);
+ }
+
+ [StackTraceHidden]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ValidateStartIndexAndLength(int startIndex, int length)
+ {
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)startIndex + (ulong)(uint)length > (ulong)(uint)this.Length)
+ ValidateStartIndexAndLength_Throw(startIndex, length);
+#else
+ if ((uint)startIndex > (uint)this.Length || (uint)length > (uint)(this.Length - startIndex))
+ ValidateStartIndexAndLength_Throw(startIndex, length);
+#endif
+ }
+
+ [StackTraceHidden]
+ private void ValidateStartIndexAndLength_Throw(int startIndex, int length)
+ {
+ throw new ArgumentOutOfRangeException(paramName: ((uint)startIndex > (uint)this.Length) ? nameof(startIndex) : nameof(length));
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs b/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs
new file mode 100644
index 0000000000..0373cdd4fd
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs
@@ -0,0 +1,93 @@
+// 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;
+using System.Text;
+using System.Text.Unicode;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ // Ordinal search
+ public bool Contains(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && Contains(result);
+ }
+
+ // Ordinal search
+ public bool Contains(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return SpanHelpers.IndexOf(
+ ref DangerousGetMutableReference(), Length,
+ ref MemoryMarshal.GetReference(runeBytes), runeBytesWritten) >= 0;
+ }
+
+ // Ordinal search
+ public bool EndsWith(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && EndsWith(result);
+ }
+
+ // Ordinal search
+ public bool EndsWith(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return this.AsBytes().EndsWith(runeBytes.Slice(0, runeBytesWritten));
+ }
+
+ // Ordinal search
+ public int IndexOf(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) ? IndexOf(result) : -1;
+ }
+
+ // Ordinal search
+ public int IndexOf(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return SpanHelpers.IndexOf(
+ ref DangerousGetMutableReference(), Length,
+ ref MemoryMarshal.GetReference(runeBytes), runeBytesWritten);
+ }
+
+ // Ordinal search
+ public bool StartsWith(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && StartsWith(result);
+ }
+
+ // Ordinal search
+ public bool StartsWith(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return this.AsBytes().StartsWith(runeBytes.Slice(0, runeBytesWritten));
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs
new file mode 100644
index 0000000000..1a4357a06f
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.cs
@@ -0,0 +1,252 @@
+// 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.Text;
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ /// <summary>
+ /// Represents an immutable string of UTF-8 code units.
+ /// </summary>
+ public sealed partial class Utf8String : IEquatable<Utf8String>
+ {
+ /*
+ * STATIC FIELDS
+ */
+
+ public static readonly Utf8String Empty = FastAllocate(0);
+
+ /*
+ * INSTANCE FIELDS
+ * Do not reorder these fields. They must match the layout of Utf8StringObject in object.h.
+ */
+
+ private readonly int _length;
+ private readonly byte _firstByte;
+
+ /*
+ * OPERATORS
+ */
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances for equality using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool operator ==(Utf8String left, Utf8String right) => Equals(left, right);
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances for inequality using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool operator !=(Utf8String left, Utf8String right) => !Equals(left, right);
+
+ /// <summary>
+ /// Projects a <see cref="Utf8String"/> instance as a <see cref="ReadOnlySpan{Byte}"/>.
+ /// </summary>
+ public static explicit operator ReadOnlySpan<byte>(Utf8String value) => value.AsBytes();
+
+ /// <summary>
+ /// Projects a <see cref="Utf8String"/> instance as a <see cref="ReadOnlySpan{Char8}"/>.
+ /// </summary>
+ public static implicit operator ReadOnlySpan<Char8>(Utf8String value) => value.AsSpan();
+
+ /*
+ * INSTANCE PROPERTIES
+ */
+
+ /// <summary>
+ /// Returns the length (in UTF-8 code units) of this instance.
+ /// </summary>
+ public int Length => _length;
+
+ /*
+ * INSTANCE INDEXERS
+ */
+
+ /// <summary>
+ /// Gets the <see cref="Char8"/> at the specified position.
+ /// </summary>
+ public Char8 this[int index]
+ {
+ get
+ {
+ // Just like String, we don't allow indexing into the null terminator itself.
+
+ if ((uint)index >= (uint)Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+
+ return Unsafe.Add(ref DangerousGetMutableReference(), index);
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="Char8"/> at the specified position.
+ /// </summary>
+ public Char8 this[Index index]
+ {
+ get
+ {
+ // Just like String, we don't allow indexing into the null terminator itself.
+
+ int actualIndex = index.GetOffset(Length);
+ return this[actualIndex];
+ }
+ }
+
+ /// <summary>
+ /// Gets a substring of this <see cref="Utf8String"/> based on the provided <paramref name="range"/>.
+ /// </summary>
+ public Utf8String this[Range range] => Substring(range);
+
+ /*
+ * METHODS
+ */
+
+ /// <summary>
+ /// Returns a <em>mutable</em> reference to the first byte of this <see cref="Utf8String"/>
+ /// (or the null terminator if the string is empty).
+ /// </summary>
+ /// <returns></returns>
+ internal ref byte DangerousGetMutableReference() => ref Unsafe.AsRef(in _firstByte);
+
+ /// <summary>
+ /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
+ /// of this <see cref="Utf8String"/> instance. The index is not bounds-checked.
+ /// </summary>
+ internal ref byte DangerousGetMutableReference(int index)
+ {
+ // Allow retrieving references to the null terminator.
+ Debug.Assert((uint)index <= (uint)Length, "Caller should've performed bounds checking.");
+
+ return ref Unsafe.Add(ref DangerousGetMutableReference(), index);
+ }
+
+ /// <summary>
+ /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ return obj is Utf8String other && this.Equals(other);
+ }
+
+ /// <summary>
+ /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public bool Equals(Utf8String value)
+ {
+ // First, a very quick check for referential equality.
+
+ if (ReferenceEquals(this, value))
+ {
+ return true;
+ }
+
+ // Otherwise, perform a simple bitwise equality check.
+
+ return !(value is null)
+ && this.Length == value.Length
+ && SpanHelpers.SequenceEqual(ref this.DangerousGetMutableReference(), ref value.DangerousGetMutableReference(), (uint)Length);
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool Equals(Utf8String left, Utf8String right)
+ {
+ // First, a very quick check for referential equality.
+
+ if (ReferenceEquals(left, right))
+ {
+ return true;
+ }
+
+ // Otherwise, perform a simple bitwise equality check.
+
+ return !(left is null)
+ && !(right is null)
+ && left.Length == right.Length
+ && SpanHelpers.SequenceEqual(ref left.DangerousGetMutableReference(), ref right.DangerousGetMutableReference(), (uint)left.Length);
+ }
+
+ /// <summary>
+ /// Returns a hash code using a <see cref="StringComparison.Ordinal"/> comparison.
+ /// </summary>
+ public override int GetHashCode()
+ {
+ // TODO_UTF8STRING: Consider whether this should use a different seed than String.GetHashCode.
+
+ ulong seed = Marvin.DefaultSeed;
+ return Marvin.ComputeHash32(ref DangerousGetMutableReference(), _length /* in bytes */, (uint)seed, (uint)(seed >> 32));
+ }
+
+ /// <summary>
+ /// Gets an immutable reference that can be used in a <see langword="fixed"/> statement. The resulting
+ /// reference can be pinned and used as a null-terminated <em>LPCUTF8STR</em>.
+ /// </summary>
+ /// <remarks>
+ /// If this <see cref="Utf8String"/> instance is empty, returns a reference to the null terminator.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)] // for compiler use only
+ public ref readonly byte GetPinnableReference() => ref _firstByte;
+
+ /// <summary>
+ /// Returns <see langword="true"/> if <paramref name="value"/> is <see langword="null"/> or zero length;
+ /// <see langword="false"/> otherwise.
+ /// </summary>
+ public static bool IsNullOrEmpty(Utf8String value)
+ {
+ // Copied from String.IsNullOrEmpty. See that method for detailed comments on why this pattern is used.
+ return (value is null || 0u >= (uint)value.Length) ? true : false;
+ }
+
+ /// <summary>
+ /// Returns the entire <see cref="Utf8String"/> as an array of bytes.
+ /// </summary>
+ public byte[] ToByteArray()
+ {
+ if (Length == 0)
+ {
+ return Array.Empty<byte>();
+ }
+
+ byte[] bytes = new byte[Length];
+ Buffer.Memmove(ref bytes.GetRawSzArrayData(), ref DangerousGetMutableReference(), (uint)Length);
+ return bytes;
+ }
+
+ /// <summary>
+ /// Returns a substring of this <see cref="Utf8String"/> as an array of bytes.
+ /// </summary>
+ public byte[] ToByteArray(int startIndex, int length)
+ {
+ ValidateStartIndexAndLength(startIndex, length);
+
+ if (length == 0)
+ {
+ return Array.Empty<byte>();
+ }
+
+ byte[] bytes = new byte[length];
+ Buffer.Memmove(ref bytes.GetRawSzArrayData(), ref DangerousGetMutableReference(startIndex), (uint)length);
+ return bytes;
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Utf8String"/> instance to a <see cref="string"/>.
+ /// </summary>
+ /// <remarks>
+ /// Invalid subsequences are replaced with U+FFFD during conversion.
+ /// </remarks>
+ public override string ToString()
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref DangerousGetMutableReference(), Length));
+ }
+ }
+}
diff --git a/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/ToolBox/SOS/Strike/apollososdocs.txt
index 6ed1dcc9e2..d2ac481ef1 100644
--- a/src/ToolBox/SOS/Strike/apollososdocs.txt
+++ b/src/ToolBox/SOS/Strike/apollososdocs.txt
@@ -1208,8 +1208,8 @@ method of my application. How can I do this?"
see the breakpoint listed.
You can specify breakpoints by file and line number if:
- a) You have some version of .Net Framework installed on your machine. Any OS from
- Vista onwards should have .Net Framework installed by default.
+ a) You have some version of .NET Framework installed on your machine. Any OS from
+ Vista onwards should have .NET Framework installed by default.
b) You have PDBs for the managed modules that need breakpoints, and your symbol
path points to those PDBs.
This is often easier than module and method name syntax. For example:
diff --git a/src/ToolBox/SOS/Strike/sosdocs.txt b/src/ToolBox/SOS/Strike/sosdocs.txt
index cf3f079a76..f03d0731f4 100644
--- a/src/ToolBox/SOS/Strike/sosdocs.txt
+++ b/src/ToolBox/SOS/Strike/sosdocs.txt
@@ -1198,8 +1198,8 @@ method of my application. How can I do this?"
see the breakpoint listed.
You can specify breakpoints by file and line number if:
- a) You have some version of .Net Framework installed on your machine. Any OS from
- Vista onwards should have .Net Framework installed by default.
+ a) You have some version of .NET Framework installed on your machine. Any OS from
+ Vista onwards should have .NET Framework installed by default.
b) You have PDBs for the managed modules that need breakpoints, and your symbol
path points to those PDBs.
This is often easier than module and method name syntax. For example:
diff --git a/src/classlibnative/bcltype/objectnative.cpp b/src/classlibnative/bcltype/objectnative.cpp
index a90a37a692..64914d8807 100644
--- a/src/classlibnative/bcltype/objectnative.cpp
+++ b/src/classlibnative/bcltype/objectnative.cpp
@@ -253,6 +253,9 @@ FCIMPL1(Object*, ObjectNative::Clone, Object* pThisUNSAFE)
// assert that String has overloaded the Clone() method
_ASSERTE(pMT != g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+ _ASSERTE(pMT != g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
if (pMT->IsArray()) {
refClone = DupArrayForCloning((BASEARRAYREF)refThis);
diff --git a/src/debug/daccess/daccess.cpp b/src/debug/daccess/daccess.cpp
index a5e1387caf..237a9b67da 100644
--- a/src/debug/daccess/daccess.cpp
+++ b/src/debug/daccess/daccess.cpp
@@ -7554,8 +7554,8 @@ BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE h
#ifdef FEATURE_PAL
// UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids
- *pPId = (DWORD)hProcess;
- *pThreadId = (DWORD)hThread;
+ *pPId = (DWORD)(SIZE_T)hProcess;
+ *pThreadId = (DWORD)(SIZE_T)hThread;
#else
#if !defined(FEATURE_CORESYSTEM)
HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll"));
diff --git a/src/debug/debug-pal/CMakeLists.txt b/src/debug/debug-pal/CMakeLists.txt
index 83e400c28f..c1b5b443a9 100644
--- a/src/debug/debug-pal/CMakeLists.txt
+++ b/src/debug/debug-pal/CMakeLists.txt
@@ -11,6 +11,7 @@ if(WIN32)
include_directories(../../inc) #needed for warning control
set(TWO_WAY_PIPE_SOURCES
+ win/diagnosticsipc.cpp
win/twowaypipe.cpp
win/processdescriptor.cpp
)
@@ -24,6 +25,7 @@ if(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-D_POSIX_C_SOURCE=200809L)
set(TWO_WAY_PIPE_SOURCES
+ unix/diagnosticsipc.cpp
unix/twowaypipe.cpp
unix/processdescriptor.cpp
)
diff --git a/src/debug/debug-pal/unix/diagnosticsipc.cpp b/src/debug/debug-pal/unix/diagnosticsipc.cpp
new file mode 100644
index 0000000000..182dff01be
--- /dev/null
+++ b/src/debug/debug-pal/unix/diagnosticsipc.cpp
@@ -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.
+
+#include <pal.h>
+#include <pal_assert.h>
+#include <new>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "diagnosticsipc.h"
+#include "processdescriptor.h"
+
+IpcStream::DiagnosticsIpc::DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress) :
+ _serverSocket(serverSocket),
+ _pServerAddress(new (std::nothrow) sockaddr_un)
+{
+ _ASSERTE(_pServerAddress != nullptr);
+ _ASSERTE(_serverSocket != -1);
+ _ASSERTE(pServerAddress != nullptr);
+
+ if (_pServerAddress == nullptr || pServerAddress == nullptr)
+ return;
+ memcpy(_pServerAddress, pServerAddress, sizeof(sockaddr_un));
+}
+
+IpcStream::DiagnosticsIpc::~DiagnosticsIpc()
+{
+ if (_serverSocket != -1)
+ {
+ const int fSuccessClose = ::close(_serverSocket);
+ _ASSERTE(fSuccessClose != -1); // TODO: Add error handling?
+
+ const int fSuccessUnlink = ::unlink(_pServerAddress->sun_path);
+ _ASSERTE(fSuccessUnlink != -1); // TODO: Add error handling?
+
+ delete _pServerAddress;
+ }
+}
+
+IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback)
+{
+ const int serverSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (serverSocket == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(serverSocket != -1);
+ return nullptr;
+ }
+
+ sockaddr_un serverAddress{};
+ serverAddress.sun_family = AF_UNIX;
+ const ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess();
+ PAL_GetTransportName(
+ sizeof(serverAddress.sun_path),
+ serverAddress.sun_path,
+ pIpcName,
+ pd.m_Pid,
+ pd.m_ApplicationGroupId,
+ "socket");
+
+ const int fSuccessBind = ::bind(serverSocket, (sockaddr *)&serverAddress, sizeof(serverAddress));
+ if (fSuccessBind == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(fSuccessBind != -1);
+ return nullptr;
+ }
+
+ return new IpcStream::DiagnosticsIpc(serverSocket, &serverAddress);
+}
+
+IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
+{
+ if (::listen(_serverSocket, /* backlog */ 255) == -1)
+ return nullptr;
+ sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ const int clientSocket = ::accept(_serverSocket, (sockaddr *)&from, &fromlen);
+ if (clientSocket == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ return nullptr;
+ }
+
+ auto pIpcStream = new (std::nothrow) IpcStream(clientSocket);
+ if (pIpcStream == nullptr && callback != nullptr)
+ callback("Failed to allocate an IpcStream object.", 1);
+ return pIpcStream;
+}
+
+IpcStream::~IpcStream()
+{
+ if (_clientSocket != -1)
+ {
+ const int fSuccessClose = ::close(_clientSocket);
+ _ASSERTE(fSuccessClose != -1);
+ }
+}
+
+bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const
+{
+ _ASSERTE(lpBuffer != nullptr);
+
+ const ssize_t ssize = ::recv(_clientSocket, lpBuffer, nBytesToRead, 0);
+ const bool fSuccess = ssize != -1;
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesRead = static_cast<uint32_t>(ssize);
+ return fSuccess;
+}
+
+bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ _ASSERTE(lpBuffer != nullptr);
+
+ const ssize_t ssize = ::send(_clientSocket, lpBuffer, nBytesToWrite, 0);
+ const bool fSuccess = ssize != -1;
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesWritten = static_cast<uint32_t>(ssize);
+ return fSuccess;
+}
+
+bool IpcStream::Flush() const
+{
+ // fsync - http://man7.org/linux/man-pages/man2/fsync.2.html ???
+ return true;
+}
diff --git a/src/debug/debug-pal/win/diagnosticsipc.cpp b/src/debug/debug-pal/win/diagnosticsipc.cpp
new file mode 100644
index 0000000000..7d9f84e7e5
--- /dev/null
+++ b/src/debug/debug-pal/win/diagnosticsipc.cpp
@@ -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.
+
+#include <assert.h>
+#include <new>
+#include <stdio.h>
+#include "diagnosticsipc.h"
+
+IpcStream::DiagnosticsIpc::DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength])
+{
+ memcpy(_pNamedPipeName, namedPipeName, sizeof(_pNamedPipeName));
+}
+
+IpcStream::DiagnosticsIpc::~DiagnosticsIpc()
+{
+}
+
+IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback)
+{
+ assert(pIpcName != nullptr);
+ if (pIpcName == nullptr)
+ return nullptr;
+
+ char namedPipeName[MaxNamedPipeNameLength]{};
+ const int nCharactersWritten = sprintf_s(
+ namedPipeName,
+ sizeof(namedPipeName),
+ "\\\\.\\pipe\\%s-%d",
+ pIpcName,
+ ::GetCurrentProcessId());
+
+ if (nCharactersWritten == -1)
+ {
+ if (callback != nullptr)
+ callback("Failed to generate the named pipe name", nCharactersWritten);
+ assert(nCharactersWritten != -1);
+ return nullptr;
+ }
+
+ return new IpcStream::DiagnosticsIpc(namedPipeName);
+}
+
+IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
+{
+ const uint32_t nInBufferSize = 16 * 1024;
+ const uint32_t nOutBufferSize = 16 * 1024;
+ HANDLE hPipe = ::CreateNamedPipeA(
+ _pNamedPipeName, // pipe name
+ PIPE_ACCESS_DUPLEX/* | FILE_FLAG_OVERLAPPED*/, // read/write access
+ PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, // message type pipe, message-read and blocking mode
+ PIPE_UNLIMITED_INSTANCES, // max. instances
+ nOutBufferSize, // output buffer size
+ nInBufferSize, // input buffer size
+ 0, // default client time-out
+ NULL); // default security attribute
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ if (callback != nullptr)
+ callback("Failed to create an instance of a named pipe.", ::GetLastError());
+ return nullptr;
+ }
+
+ const BOOL fSuccess = ::ConnectNamedPipe(hPipe, NULL) != 0;
+ const DWORD errorCode = ::GetLastError();
+ if (!fSuccess && (errorCode != ERROR_PIPE_CONNECTED))
+ {
+ if (callback != nullptr)
+ callback("Failed to wait for a client process to connect.", errorCode);
+ return nullptr;
+ }
+
+ auto pIpcStream = new (std::nothrow) IpcStream(hPipe);
+ if (pIpcStream == nullptr && callback != nullptr)
+ callback("Failed to allocate an IpcStream object.", 1);
+ return pIpcStream;
+}
+
+IpcStream::~IpcStream()
+{
+ if (_hPipe != INVALID_HANDLE_VALUE)
+ {
+ const BOOL fSuccessDisconnectNamedPipe = ::DisconnectNamedPipe(_hPipe);
+ assert(fSuccessDisconnectNamedPipe != 0);
+
+ const BOOL fSuccessCloseHandle = ::CloseHandle(_hPipe);
+ assert(CloseHandle != 0);
+ }
+}
+
+bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const
+{
+ assert(lpBuffer != nullptr);
+
+ DWORD nNumberOfBytesRead = 0;
+ const bool fSuccess = ::ReadFile(
+ _hPipe, // handle to pipe
+ lpBuffer, // buffer to receive data
+ nBytesToRead, // size of buffer
+ &nNumberOfBytesRead, // number of bytes read
+ NULL) != 0; // not overlapped I/O
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesRead = static_cast<uint32_t>(nNumberOfBytesRead);
+ return fSuccess;
+}
+
+bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ assert(lpBuffer != nullptr);
+
+ DWORD nNumberOfBytesWritten = 0;
+ const bool fSuccess = ::WriteFile(
+ _hPipe, // handle to pipe
+ lpBuffer, // buffer to write from
+ nBytesToWrite, // number of bytes to write
+ &nNumberOfBytesWritten, // number of bytes written
+ NULL) != 0; // not overlapped I/O
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesWritten = static_cast<uint32_t>(nNumberOfBytesWritten);
+ return fSuccess;
+}
+
+bool IpcStream::Flush() const
+{
+ const bool fSuccess = ::FlushFileBuffers(_hPipe) != 0;
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+ return fSuccess;
+}
diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp
index 63aa8c256c..cb083c3214 100644
--- a/src/debug/ee/debugger.cpp
+++ b/src/debug/ee/debugger.cpp
@@ -977,7 +977,7 @@ Debugger::Debugger()
//------------------------------------------------------------------------------
// Metadata data structure version numbers
//
- // 1 - initial state of the layouts ( .Net 4.5.2 )
+ // 1 - initial state of the layouts ( .NET Framework 4.5.2 )
//
// as data structure layouts change, add a new version number
// and comment the changes
@@ -11100,7 +11100,7 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent)
{
// Get the appdomain
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
- ADIndex appDomainIndex = ADIndex(reinterpret_cast<DWORD>(mgr->GetHandleContext(objectHandle)));
+ ADIndex appDomainIndex = ADIndex((DWORD)(SIZE_T)(mgr->GetHandleContext(objectHandle)));
pAppDomain = SystemDomain::GetAppDomainAtIndex(appDomainIndex);
_ASSERTE(pAppDomain != NULL);
diff --git a/src/debug/inc/diagnosticsipc.h b/src/debug/inc/diagnosticsipc.h
new file mode 100644
index 0000000000..81789f0803
--- /dev/null
+++ b/src/debug/inc/diagnosticsipc.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef __DIAGNOSTICS_IPC_H__
+#define __DIAGNOSTICS_IPC_H__
+
+#include <stdint.h>
+
+#ifdef FEATURE_PAL
+ struct sockaddr_un;
+#else
+ #include <Windows.h>
+#endif /* FEATURE_PAL */
+
+typedef void (*ErrorCallback)(const char *szMessage, uint32_t code);
+
+class IpcStream final
+{
+public:
+ ~IpcStream();
+ bool Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const;
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
+ bool Flush() const;
+
+ class DiagnosticsIpc final
+ {
+ public:
+ static DiagnosticsIpc *Create(const char *const pIpcName, ErrorCallback callback = nullptr);
+ ~DiagnosticsIpc();
+ IpcStream *Accept(ErrorCallback callback = nullptr) const;
+
+ private:
+
+#ifdef FEATURE_PAL
+ DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress);
+ const int _serverSocket = -1;
+ sockaddr_un *const _pServerAddress;
+#else
+ static const uint32_t MaxNamedPipeNameLength = 256;
+ DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength]);
+ char _pNamedPipeName[MaxNamedPipeNameLength]; // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createnamedpipea
+#endif /* FEATURE_PAL */
+
+ DiagnosticsIpc() = delete;
+ DiagnosticsIpc(const DiagnosticsIpc &src) = delete;
+ DiagnosticsIpc(DiagnosticsIpc &&src) = delete;
+ DiagnosticsIpc &operator=(const DiagnosticsIpc &rhs) = delete;
+ DiagnosticsIpc &&operator=(DiagnosticsIpc &&rhs) = delete;
+ };
+
+private:
+#ifdef FEATURE_PAL
+ int _clientSocket = -1;
+ IpcStream(int clientSocket) : _clientSocket(clientSocket) {}
+#else
+ HANDLE _hPipe = INVALID_HANDLE_VALUE;
+ IpcStream(HANDLE hPipe) : _hPipe(hPipe) {}
+#endif /* FEATURE_PAL */
+
+ IpcStream() = delete;
+ IpcStream(const IpcStream &src) = delete;
+ IpcStream(IpcStream &&src) = delete;
+ IpcStream &operator=(const IpcStream &rhs) = delete;
+ IpcStream &&operator=(IpcStream &&rhs) = delete;
+};
+
+#endif // __DIAGNOSTICS_IPC_H__
diff --git a/src/gc/sample/gcenv.ee.cpp b/src/gc/sample/gcenv.ee.cpp
index 9a4e9c9a74..687fd5624b 100644
--- a/src/gc/sample/gcenv.ee.cpp
+++ b/src/gc/sample/gcenv.ee.cpp
@@ -100,7 +100,11 @@ uint32_t CLREventStatic::Wait(uint32_t dwMilliseconds, bool bAlertable)
return result;
}
+#ifndef __GNUC__
__declspec(thread) Thread * pCurrentThread;
+#else // !__GNUC__
+thread_local Thread * pCurrentThread;
+#endif // !__GNUC__
Thread * GetThread()
{
diff --git a/src/gc/unix/cgroup.cpp b/src/gc/unix/cgroup.cpp
index b66323a3b9..88eb415474 100644
--- a/src/gc/unix/cgroup.cpp
+++ b/src/gc/unix/cgroup.cpp
@@ -434,10 +434,19 @@ void CleanupCGroup()
size_t GetRestrictedPhysicalMemoryLimit()
{
- size_t physical_memory_limit;
+ size_t physical_memory_limit = 0;
if (!CGroup::GetPhysicalMemoryLimit(&physical_memory_limit))
- physical_memory_limit = SIZE_T_MAX;
+ return 0;
+
+ // If there's no memory limit specified on the container this
+ // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
+ // 4k which is a common page size). So we know we are not
+ // running in a memory restricted environment.
+ if (physical_memory_limit > 0x7FFFFFFF00000000)
+ {
+ return 0;
+ }
struct rlimit curr_rlimit;
size_t rlimit_soft_limit = (size_t)RLIM_INFINITY;
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index ed09e89f26..572fc3b12f 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -449,7 +449,7 @@ the processors enabled.
--*/
static uintptr_t GetFullAffinityMask(int cpuCount)
{
- if (cpuCount < sizeof(uintptr_t) * 8)
+ if ((size_t)cpuCount < sizeof(uintptr_t) * 8)
{
return ((uintptr_t)1 << cpuCount) - 1;
}
diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index f032405c66..f7d069c874 100644
--- a/src/gc/windows/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp
@@ -286,16 +286,21 @@ static size_t GetRestrictedPhysicalMemoryLimit()
if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
- job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
- job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
+ if ((job_memory_limit != (size_t)UINTPTR_MAX) ||
+ (job_process_memory_limit != (size_t)UINTPTR_MAX) ||
+ (job_workingset_limit != (size_t)UINTPTR_MAX))
+ {
+ job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
+ job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
- MEMORYSTATUSEX ms;
- ::GetProcessMemoryLoad(&ms);
- total_virtual = ms.ullTotalVirtual;
- total_physical = ms.ullAvailPhys;
+ MEMORYSTATUSEX ms;
+ ::GetProcessMemoryLoad(&ms);
+ total_virtual = ms.ullTotalVirtual;
+ total_physical = ms.ullAvailPhys;
- // A sanity check in case someone set a larger limit than there is actual physical memory.
- job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ // A sanity check in case someone set a larger limit than there is actual physical memory.
+ job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ }
}
}
diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h
index fc5be15590..cec6d74dd7 100644
--- a/src/inc/dacvars.h
+++ b/src/inc/dacvars.h
@@ -168,6 +168,9 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pObjectClass, ::g_pObjectClass
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pRuntimeTypeClass, ::g_pRuntimeTypeClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pCanonMethodTableClass, ::g_pCanonMethodTableClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pStringClass, ::g_pStringClass)
+#ifdef FEATURE_UTF8STRING
+DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pUtf8StringClass, ::g_pUtf8StringClass)
+#endif // FEATURE_UTF8STRING
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pArrayClass, ::g_pArrayClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pSZArrayHelperClass, ::g_pSZArrayHelperClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pNullableClass, ::g_pNullableClass)
diff --git a/src/inc/mscorsvc.idl b/src/inc/mscorsvc.idl
index 48eada4f6f..33ded4247f 100644
--- a/src/inc/mscorsvc.idl
+++ b/src/inc/mscorsvc.idl
@@ -2,7 +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.
/* -------------------------------------------------------------------------- *
- * Microsoft .Net Framework Service
+ * Microsoft .NET Framework Service
* -------------------------------------------------------------------------- */
#ifndef IN_MSCOREE
diff --git a/src/inc/registrywrapper.h b/src/inc/registrywrapper.h
index 6b284506bb..0fbeee2356 100644
--- a/src/inc/registrywrapper.h
+++ b/src/inc/registrywrapper.h
@@ -4,7 +4,7 @@
//*****************************************************************************
// File: registrywrapper.h
//
-// Wrapper around Win32 Registry Functions allowing redirection of .Net
+// Wrapper around Win32 Registry Functions allowing redirection of .NET
// Framework root registry location
//
//*****************************************************************************
diff --git a/src/inc/stresslog.h b/src/inc/stresslog.h
index dd8c029b0a..8f98f9a7b2 100644
--- a/src/inc/stresslog.h
+++ b/src/inc/stresslog.h
@@ -127,7 +127,7 @@
LOG((facility, level, msg, data1, data2, data3, data4, data5, data6, data7)); \
} while(0)
-#define STRESS_LOG_COND0(facility, level, msg) do { \
+#define STRESS_LOG_COND0(facility, level, cond, msg) do { \
if (StressLog::LogOn(facility, level) && (cond)) \
StressLog::LogMsg(level, facility, 0, msg); \
LOG((facility, level, msg)); \
diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h
index a3123efa84..6f103c4da5 100644
--- a/src/inc/utilcode.h
+++ b/src/inc/utilcode.h
@@ -4133,12 +4133,6 @@ inline HRESULT FakeCoCreateInstance(REFCLSID rclsid,
return FakeCoCreateInstanceEx(rclsid, NULL, riid, ppv, NULL);
};
-HRESULT FakeCoCallDllGetClassObject(REFCLSID rclsid,
- LPCWSTR wszDllPath,
- REFIID riid,
- void ** ppv,
- HMODULE * phmodDll);
-
//*****************************************************************************
// Gets the directory based on the location of the module. This routine
// is called at COR setup time. Set is called during EEStartup and by the
@@ -5212,12 +5206,7 @@ namespace Reg
#ifdef FEATURE_COMINTEROP
namespace Com
{
- HRESULT FindServerUsingCLSID(REFCLSID rclsid, SString & ssServerName);
- HRESULT FindServerUsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszServerName);
HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name);
- HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszInprocServer32Name);
- BOOL IsMscoreeInprocServer32(const SString & ssInprocServer32Name);
- BOOL CLSIDHasMscoreeAsInprocServer32(REFCLSID rclsid);
}
#endif // FEATURE_COMINTEROP
diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt
index 5b98b4f507..013b8974ed 100644
--- a/src/jit/CMakeLists.txt
+++ b/src/jit/CMakeLists.txt
@@ -4,6 +4,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include_directories("./jitstd")
include_directories("../inc")
+if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ add_compile_options(-fpermissive)
+ add_compile_options(-Wno-error)
+endif()
+
if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND NOT CLR_CMAKE_PLATFORM_UNIX))
add_definitions(-DFEATURE_SIMD)
add_definitions(-DFEATURE_HW_INTRINSICS)
diff --git a/src/jit/bitset.h b/src/jit/bitset.h
index a0192e62e8..bddc2ea532 100644
--- a/src/jit/bitset.h
+++ b/src/jit/bitset.h
@@ -30,7 +30,7 @@ public:
{
unsigned res = 0;
// We process "u" in 4-bit nibbles, hence the "*2" below.
- for (int i = 0; i < sizeof(T) * 2; i++)
+ for (unsigned int i = 0; i < sizeof(T) * 2; i++)
{
res += BitCountTable[u & 0xf];
u >>= 4;
diff --git a/src/jit/bitsetasshortlong.h b/src/jit/bitsetasshortlong.h
index 128c0aab3f..17e0e3a69c 100644
--- a/src/jit/bitsetasshortlong.h
+++ b/src/jit/bitsetasshortlong.h
@@ -313,7 +313,10 @@ public:
{
if (IsShort(env))
{
- (size_t&)bs1 &= (size_t)bs2;
+ size_t val = (size_t)bs1;
+
+ val &= (size_t)bs2;
+ bs1 = (BitSetShortLongRep)val;
}
else
{
diff --git a/src/jit/codegen.h b/src/jit/codegen.h
index c4af197d2b..be20865c0e 100644
--- a/src/jit/codegen.h
+++ b/src/jit/codegen.h
@@ -513,7 +513,7 @@ protected:
#ifdef _TARGET_ARM64_
virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value);
- virtual bool IsSaveFpLrWithAllCalleeSavedRegisters();
+ virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const;
bool genSaveFpLrWithAllCalleeSavedRegisters;
#endif // _TARGET_ARM64_
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 9d1959e850..9cfaccef08 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -2198,7 +2198,7 @@ void CodeGen::genLclHeap(GenTree* tree)
{
regCnt = tree->ExtractTempReg();
}
- genSetRegToIcon(regCnt, amount, ((int)amount == amount) ? TYP_INT : TYP_LONG);
+ genSetRegToIcon(regCnt, amount, ((unsigned int)amount == amount) ? TYP_INT : TYP_LONG);
}
if (compiler->info.compInitMem)
@@ -2484,7 +2484,7 @@ void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
}
assert(dstAddr->isUsedFromReg());
- assert(initVal->isUsedFromReg() && !initVal->IsIntegralConst(0) || initVal->IsIntegralConst(0));
+ assert((initVal->isUsedFromReg() && !initVal->IsIntegralConst(0)) || initVal->IsIntegralConst(0));
assert(size != 0);
assert(size <= INITBLK_UNROLL_LIMIT);
@@ -3695,7 +3695,7 @@ void CodeGen::genCodeForJumpCompare(GenTreeOp* tree)
// genSPtoFPdelta - return offset from the stack pointer (Initial-SP) to the frame pointer. The frame pointer
// will point to the saved frame pointer slot (i.e., there will be frame pointer chaining).
//
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
assert(isFramePointerUsed());
int delta = -1; // initialization to illegal value
@@ -3725,7 +3725,7 @@ int CodeGenInterface::genSPtoFPdelta()
// Total frame size
//
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
// For varargs functions, we home all the incoming register arguments. They are not
// included in the compCalleeRegsPushed count. This is like prespill on ARM32, but
@@ -3748,7 +3748,7 @@ int CodeGenInterface::genTotalFrameSize()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta;
@@ -3764,7 +3764,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
@@ -3788,7 +3788,7 @@ void CodeGen::SetSaveFpLrWithAllCalleeSavedRegisters(bool value)
// IsSaveFpLrWithAllCalleeSavedRegisters - Return the value that indicates where FP/LR registers
// are stored in the prolog.
//
-bool CodeGen::IsSaveFpLrWithAllCalleeSavedRegisters()
+bool CodeGen::IsSaveFpLrWithAllCalleeSavedRegisters() const
{
return genSaveFpLrWithAllCalleeSavedRegisters;
}
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index a45710330f..b90cf5548d 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -191,7 +191,7 @@ void CodeGenInterface::genMarkTreeInReg(GenTree* tree, regNumber reg)
// Return value:
// Frame size
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
assert(!IsUninitialized(compiler->compCalleeRegsPushed));
@@ -208,7 +208,7 @@ int CodeGenInterface::genTotalFrameSize()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
assert(isFramePointerUsed());
@@ -227,7 +227,7 @@ int CodeGenInterface::genSPtoFPdelta()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta = 0;
@@ -255,7 +255,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
@@ -10816,7 +10816,7 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel)
default:
- if (offsx != ICorDebugInfo::NO_MAPPING)
+ if (offsx != (IL_OFFSETX)ICorDebugInfo::NO_MAPPING)
{
noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize);
}
diff --git a/src/jit/codegeninterface.h b/src/jit/codegeninterface.h
index 25027c1104..94fd8f4635 100644
--- a/src/jit/codegeninterface.h
+++ b/src/jit/codegeninterface.h
@@ -171,14 +171,14 @@ public:
}
public:
- int genCallerSPtoFPdelta();
- int genCallerSPtoInitialSPdelta();
- int genSPtoFPdelta();
- int genTotalFrameSize();
+ int genCallerSPtoFPdelta() const;
+ int genCallerSPtoInitialSPdelta() const;
+ int genSPtoFPdelta() const;
+ int genTotalFrameSize() const;
#ifdef _TARGET_ARM64_
virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value) = 0;
- virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() = 0;
+ virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const = 0;
#endif // _TARGET_ARM64_
regNumber genGetThisArgReg(GenTreeCall* call) const;
@@ -304,7 +304,7 @@ public:
TempDsc* getSpillTempDsc(GenTree* tree);
public:
- emitter* getEmitter()
+ emitter* getEmitter() const
{
return m_cgEmitter;
}
@@ -512,12 +512,15 @@ public:
// Helper functions
- bool vlIsInReg(regNumber reg);
- bool vlIsOnStk(regNumber reg, signed offset);
+ bool vlIsInReg(regNumber reg) const;
+ bool vlIsOnStk(regNumber reg, signed offset) const;
siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed);
siVarLoc(){};
+ // An overload for the equality comparator
+ static bool Equals(const siVarLoc* lhs, const siVarLoc* rhs);
+
private:
// Fill "siVarLoc" properties indicating the register position of the variable
// using "LclVarDsc" and "baseReg"/"offset" if it has a part in the stack (x64 bit float or long).
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index d7b15025a3..f332b0043a 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -6852,7 +6852,7 @@ void CodeGen::genCkfinite(GenTree* treeNode)
}
#ifdef _TARGET_AMD64_
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
int delta;
@@ -6904,7 +6904,7 @@ int CodeGenInterface::genSPtoFPdelta()
// Total frame size
//
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
assert(!IsUninitialized(compiler->compCalleeRegsPushed));
@@ -6925,7 +6925,7 @@ int CodeGenInterface::genTotalFrameSize()
// is based on a maximum delta from Initial-SP, so first we find SP, then
// compute the FP offset.
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta;
@@ -6941,7 +6941,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 489218800e..3887f0ea98 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -2523,8 +2523,8 @@ void Compiler::compSetProcessor()
#if defined(_TARGET_ARM64_)
// There is no JitFlag for Base instructions handle manually
opts.setSupportedISA(InstructionSet_Base);
-#define HARDWARE_INTRINSIC_CLASS(flag, isa) \
- if (jitFlags.IsSet(JitFlags::flag)) \
+#define HARDWARE_INTRINSIC_CLASS(flag, jit_config, isa) \
+ if (jitFlags.IsSet(JitFlags::flag) && JitConfig.jit_config()) \
opts.setSupportedISA(InstructionSet_##isa);
#include "hwintrinsiclistArm64.h"
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index d6edd6483f..eefa345e6e 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -2994,7 +2994,7 @@ public:
unsigned lvaFrameSize(FrameLayoutState curState);
// Returns the caller-SP-relative offset for the SP/FP relative offset determined by FP based.
- int lvaToCallerSPRelativeOffset(int offs, bool isFpBased);
+ int lvaToCallerSPRelativeOffset(int offs, bool isFpBased) const;
// Returns the caller-SP-relative offset for the local variable "varNum."
int lvaGetCallerSPRelativeOffset(unsigned varNum);
@@ -7088,7 +7088,7 @@ public:
// the setter on CodeGenContext directly.
__declspec(property(get = getEmitter)) emitter* genEmitter;
- emitter* getEmitter()
+ emitter* getEmitter() const
{
return codeGen->getEmitter();
}
@@ -8772,9 +8772,9 @@ public:
unsigned compArgSize; // total size of arguments in bytes (including register args (lvIsRegArg))
- unsigned compMapILargNum(unsigned ILargNum); // map accounting for hidden args
- unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args
- unsigned compMap2ILvarNum(unsigned varNum); // map accounting for hidden args
+ unsigned compMapILargNum(unsigned ILargNum); // map accounting for hidden args
+ unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args
+ unsigned compMap2ILvarNum(unsigned varNum) const; // map accounting for hidden args
//-------------------------------------------------------------------------
@@ -8844,7 +8844,7 @@ public:
#endif // LOOP_HOIST_STATS
bool compIsForImportOnly();
- bool compIsForInlining();
+ bool compIsForInlining() const;
bool compDonotInline();
#ifdef DEBUG
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index c1be6acbbe..2eb5ac7b08 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -3859,7 +3859,7 @@ inline bool Compiler::compIsForImportOnly()
* Returns true if the compiler instance is created for inlining.
*/
-inline bool Compiler::compIsForInlining()
+inline bool Compiler::compIsForInlining() const
{
return (impInlineInfo != nullptr);
}
diff --git a/src/jit/earlyprop.cpp b/src/jit/earlyprop.cpp
index 26a50d894b..693294b49c 100644
--- a/src/jit/earlyprop.cpp
+++ b/src/jit/earlyprop.cpp
@@ -497,11 +497,11 @@ void Compiler::optFoldNullCheck(GenTree* tree)
// Check for a pattern like this:
//
// =
- // / \
+ // / \.
// x comma
- // / \
+ // / \.
// nullcheck +
- // | / \
+ // | / \.
// y y const
//
//
@@ -517,9 +517,9 @@ void Compiler::optFoldNullCheck(GenTree* tree)
// and transform it into
//
// =
- // / \
+ // / \.
// x +
- // / \
+ // / \.
// y const
//
//
diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp
index 410ef6ca01..ca3fce2aad 100644
--- a/src/jit/ee_il_dll.cpp
+++ b/src/jit/ee_il_dll.cpp
@@ -163,10 +163,11 @@ void jitShutdown(bool processIsTerminating)
#ifndef FEATURE_MERGE_JIT_AND_ENGINE
+extern "C"
#ifdef FEATURE_PAL
-DLLEXPORT // For Win32 PAL LoadLibrary emulation
+ DLLEXPORT // For Win32 PAL LoadLibrary emulation
#endif
- extern "C" BOOL WINAPI
+ BOOL WINAPI
DllMain(HANDLE hInstance, DWORD dwReason, LPVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
@@ -230,7 +231,11 @@ DLLEXPORT ICorJitCompiler* __stdcall getJit()
// If you are using it more broadly in retail code, you would need to understand the
// performance implications of accessing TLS.
+#ifndef __GNUC__
__declspec(thread) void* gJitTls = nullptr;
+#else // !__GNUC__
+thread_local void* gJitTls = nullptr;
+#endif // !__GNUC__
static void* GetJitTls()
{
diff --git a/src/jit/emit.h b/src/jit/emit.h
index 876dc085fc..c5b50a5d1b 100644
--- a/src/jit/emit.h
+++ b/src/jit/emit.h
@@ -153,7 +153,7 @@ public:
// A constructor for code that needs to call it explicitly.
void Init()
{
- this->emitLocation::emitLocation();
+ *this = emitLocation();
}
void CaptureLocation(emitter* emit);
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index 4888ce27ec..3f830f6f4c 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -15283,11 +15283,11 @@ void Compiler::fgReorderBlocks()
// is more than twice the max weight of the bPrev to block edge.
//
// bPrev --> [BB04, weight 31]
- // | \
- // edgeToBlock -------------> O \
- // [min=8,max=10] V \
- // block --> [BB05, weight 10] \
- // \
+ // | \.
+ // edgeToBlock -------------> O \.
+ // [min=8,max=10] V \.
+ // block --> [BB05, weight 10] \.
+ // \.
// edgeToDest ----------------------------> O
// [min=21,max=23] |
// V
@@ -15331,10 +15331,10 @@ void Compiler::fgReorderBlocks()
// 2. Check that the weight of bPrev is at least three times more than block
//
// bPrev --> [BB04, weight 31]
- // | \
- // V \
- // block --> [BB05, weight 10] \
- // \
+ // | \.
+ // V \.
+ // block --> [BB05, weight 10] \.
+ // \.
// |
// V
// bDest ---------------> [BB08, weight 21]
@@ -21069,8 +21069,8 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
// written to or address-exposed.
assert(compThisArgAddrExposedOK && !lvaTable[info.compThisArg].lvHasILStoreOp &&
(lvaArg0Var == info.compThisArg ||
- lvaArg0Var != info.compThisArg && (lvaTable[lvaArg0Var].lvAddrExposed ||
- lvaTable[lvaArg0Var].lvHasILStoreOp || copiedForGenericsCtxt)));
+ (lvaArg0Var != info.compThisArg && (lvaTable[lvaArg0Var].lvAddrExposed ||
+ lvaTable[lvaArg0Var].lvHasILStoreOp || copiedForGenericsCtxt))));
}
}
diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp
index 4ec6f4cb8b..331796c33a 100644
--- a/src/jit/gcencode.cpp
+++ b/src/jit/gcencode.cpp
@@ -3979,7 +3979,7 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz
{
// The predicate above is true only if there is an extra generic context parameter, not for
// the case where the generic context is provided by "this."
- assert(compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
+ assert((SIZE_T)compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
GENERIC_CONTEXTPARAM_TYPE ctxtParamType = GENERIC_CONTEXTPARAM_NONE;
switch (compiler->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_MASK)
{
diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h
index dd23db486b..2421a8f033 100644
--- a/src/jit/gtlist.h
+++ b/src/jit/gtlist.h
@@ -20,12 +20,12 @@ GTNODE(NONE , char ,0,GTK_SPECIAL)
// Leaf nodes (i.e. these nodes have no sub-operands):
//-----------------------------------------------------------------------------
-GTNODE(LCL_VAR , GenTreeLclVar ,0,GTK_LEAF|GTK_LOCAL) // local variable
-GTNODE(LCL_FLD , GenTreeLclFld ,0,GTK_LEAF|GTK_LOCAL) // field in a non-primitive variable
+GTNODE(LCL_VAR , GenTreeLclVar ,0,(GTK_LEAF|GTK_LOCAL)) // local variable
+GTNODE(LCL_FLD , GenTreeLclFld ,0,(GTK_LEAF|GTK_LOCAL)) // field in a non-primitive variable
GTNODE(LCL_VAR_ADDR , GenTreeLclVar ,0,GTK_LEAF) // address of local variable
GTNODE(LCL_FLD_ADDR , GenTreeLclFld ,0,GTK_LEAF) // address of field in a non-primitive variable
-GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,GTK_UNOP|GTK_LOCAL|GTK_NOVALUE) // store to local variable
-GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,GTK_UNOP|GTK_LOCAL|GTK_NOVALUE) // store to field in a non-primitive variable
+GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,(GTK_UNOP|GTK_LOCAL|GTK_NOVALUE)) // store to local variable
+GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,(GTK_UNOP|GTK_LOCAL|GTK_NOVALUE)) // store to field in a non-primitive variable
GTNODE(CATCH_ARG , GenTree ,0,GTK_LEAF) // Exception object in a catch block
GTNODE(LABEL , GenTree ,0,GTK_LEAF) // Jump-target
GTNODE(FTN_ADDR , GenTreeFptrVal ,0,GTK_LEAF) // Address of a function
@@ -35,56 +35,56 @@ GTNODE(RET_EXPR , GenTreeRetExpr ,0,GTK_LEAF) // Place
// Constant nodes:
//-----------------------------------------------------------------------------
-GTNODE(CNS_INT , GenTreeIntCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_LNG , GenTreeLngCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_DBL , GenTreeDblCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_STR , GenTreeStrCon ,0,GTK_LEAF|GTK_CONST)
+GTNODE(CNS_INT , GenTreeIntCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_LNG , GenTreeLngCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_DBL , GenTreeDblCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_STR , GenTreeStrCon ,0,(GTK_LEAF|GTK_CONST))
//-----------------------------------------------------------------------------
// Unary operators (1 operand):
//-----------------------------------------------------------------------------
GTNODE(NOT , GenTreeOp ,0,GTK_UNOP)
-GTNODE(NOP , GenTree ,0,GTK_UNOP|GTK_NOCONTAIN)
+GTNODE(NOP , GenTree ,0,(GTK_UNOP|GTK_NOCONTAIN))
GTNODE(NEG , GenTreeOp ,0,GTK_UNOP)
GTNODE(COPY , GenTreeCopyOrReload,0,GTK_UNOP) // Copies a variable from its current location to a register that satisfies
// code generation constraints. The child is the actual lclVar node.
GTNODE(RELOAD , GenTreeCopyOrReload,0,GTK_UNOP)
-GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) // array-length
-GTNODE(INTRINSIC , GenTreeIntrinsic ,0,GTK_BINOP|GTK_EXOP) // intrinsics
+GTNODE(ARR_LENGTH , GenTreeArrLen ,0,(GTK_UNOP|GTK_EXOP)) // array-length
+GTNODE(INTRINSIC , GenTreeIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // intrinsics
-GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE)
+GTNODE(LOCKADD , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE))
GTNODE(XADD , GenTreeOp ,0,GTK_BINOP)
GTNODE(XCHG , GenTreeOp ,0,GTK_BINOP)
GTNODE(CMPXCHG , GenTreeCmpXchg ,0,GTK_SPECIAL)
-GTNODE(MEMORYBARRIER , GenTree ,0,GTK_LEAF|GTK_NOVALUE)
+GTNODE(MEMORYBARRIER , GenTree ,0,(GTK_LEAF|GTK_NOVALUE))
-GTNODE(CAST , GenTreeCast ,0,GTK_UNOP|GTK_EXOP) // conversion to another type
+GTNODE(CAST , GenTreeCast ,0,(GTK_UNOP|GTK_EXOP)) // conversion to another type
#if defined(_TARGET_ARM_)
GTNODE(BITCAST , GenTreeMultiRegOp ,0,GTK_UNOP) // reinterpretation of bits as another type
#else
GTNODE(BITCAST , GenTreeOp ,0,GTK_UNOP) // reinterpretation of bits as another type
#endif
-GTNODE(CKFINITE , GenTreeOp ,0,GTK_UNOP|GTK_NOCONTAIN) // Check for NaN
-GTNODE(LCLHEAP , GenTreeOp ,0,GTK_UNOP|GTK_NOCONTAIN) // alloca()
-GTNODE(JMP , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function
+GTNODE(CKFINITE , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // Check for NaN
+GTNODE(LCLHEAP , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // alloca()
+GTNODE(JMP , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // Jump to another function
GTNODE(ADDR , GenTreeOp ,0,GTK_UNOP) // address of
GTNODE(IND , GenTreeOp ,0,GTK_UNOP) // load indirection
-GTNODE(STOREIND , GenTreeStoreInd ,0,GTK_BINOP|GTK_NOVALUE) // store indirection
+GTNODE(STOREIND , GenTreeStoreInd ,0,(GTK_BINOP|GTK_NOVALUE)) // store indirection
// TODO-Cleanup: GT_ARR_BOUNDS_CHECK should be made a GTK_BINOP now that it has only two child nodes
-GTNODE(ARR_BOUNDS_CHECK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// array bounds check
-GTNODE(OBJ , GenTreeObj ,0,GTK_UNOP|GTK_EXOP) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
-GTNODE(STORE_OBJ , GenTreeBlk ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
+GTNODE(ARR_BOUNDS_CHECK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// array bounds check
+GTNODE(OBJ , GenTreeObj ,0,(GTK_UNOP|GTK_EXOP)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
+GTNODE(STORE_OBJ , GenTreeBlk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
GTNODE(BLK , GenTreeBlk ,0,GTK_UNOP) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
-GTNODE(STORE_BLK , GenTreeBlk ,0,GTK_BINOP|GTK_NOVALUE) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
+GTNODE(STORE_BLK , GenTreeBlk ,0,(GTK_BINOP|GTK_NOVALUE)) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
GTNODE(DYN_BLK , GenTreeBlk ,0,GTK_SPECIAL) // Dynamically sized block object
-GTNODE(STORE_DYN_BLK , GenTreeBlk ,0,GTK_SPECIAL|GTK_NOVALUE)// Dynamically sized block object
-GTNODE(BOX , GenTreeBox ,0,GTK_UNOP|GTK_EXOP|GTK_NOTLIR)
+GTNODE(STORE_DYN_BLK , GenTreeBlk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Dynamically sized block object
+GTNODE(BOX , GenTreeBox ,0,(GTK_UNOP|GTK_EXOP|GTK_NOTLIR))
#ifdef FEATURE_SIMD
-GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compare whether an index is less than the given SIMD vector length, and call CORINFO_HELP_RNGCHKFAIL if not.
+GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Compare whether an index is less than the given SIMD vector length, and call CORINFO_HELP_RNGCHKFAIL if not.
// TODO-CQ: In future may want to add a field that specifies different exceptions but we'll
// need VM assistance for that.
// TODO-CQ: It would actually be very nice to make this an unconditional throw, and expose the control flow that
@@ -92,14 +92,14 @@ GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compa
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HW_INTRINSIC_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compare whether an imm8 argument is in the valid range, and throw ArgumentOutOfRangeException if not.
+GTNODE(HW_INTRINSIC_CHK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Compare whether an imm8 argument is in the valid range, and throw ArgumentOutOfRangeException if not.
#endif
-GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP) // object allocator
+GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,(GTK_UNOP|GTK_EXOP)) // object allocator
GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk
-GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP) // Runtime handle lookup
+GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,(GTK_UNOP|GTK_EXOP)) // Runtime handle lookup
GTNODE(BSWAP , GenTreeOp ,0,GTK_UNOP) // Byte swap (32-bit or 64-bit)
GTNODE(BSWAP16 , GenTreeOp ,0,GTK_UNOP) // Byte swap (16-bit)
@@ -117,9 +117,9 @@ GTNODE(MOD , GenTreeOp ,0,GTK_BINOP)
GTNODE(UDIV , GenTreeOp ,0,GTK_BINOP)
GTNODE(UMOD , GenTreeOp ,0,GTK_BINOP)
-GTNODE(OR , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
-GTNODE(XOR , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
-GTNODE(AND , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
+GTNODE(OR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
+GTNODE(XOR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
+GTNODE(AND , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
GTNODE(LSH , GenTreeOp ,0,GTK_BINOP)
GTNODE(RSH , GenTreeOp ,0,GTK_BINOP)
@@ -131,13 +131,13 @@ GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP) // returns high bits
// the div into a MULHI + some adjustments. In codegen, we only use the
// results of the high register, and we drop the low results.
-GTNODE(ASG , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR)
-GTNODE(EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(LT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(LE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(GE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
+GTNODE(ASG , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR))
+GTNODE(EQ , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(NE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(LT , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(LE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(GE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(GT , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
// These are similar to GT_EQ/GT_NE but they generate "test" instead of "cmp" instructions.
// Currently these are generated during lowering for code like ((x & y) eq|ne 0) only on
@@ -146,21 +146,21 @@ GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
// codegen which emits a "test reg, reg" instruction, that would be more difficult to do
// during lowering because the source operand is used twice so it has to be a lclvar.
// Because of this there is no need to also add GT_TEST_LT/LE/GE/GT opers.
-GTNODE(TEST_EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(TEST_NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
+GTNODE(TEST_EQ , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(TEST_NE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
-GTNODE(COMMA , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR)
+GTNODE(COMMA , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR))
-GTNODE(QMARK , GenTreeQmark ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR)
-GTNODE(COLON , GenTreeColon ,0,GTK_BINOP|GTK_NOTLIR)
+GTNODE(QMARK , GenTreeQmark ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR))
+GTNODE(COLON , GenTreeColon ,0,(GTK_BINOP|GTK_NOTLIR))
-GTNODE(INDEX , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR) // SZ-array-element
-GTNODE(INDEX_ADDR , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP) // addr of SZ-array-element; used when
+GTNODE(INDEX , GenTreeIndex ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR)) // SZ-array-element
+GTNODE(INDEX_ADDR , GenTreeIndex ,0,(GTK_BINOP|GTK_EXOP)) // addr of SZ-array-element; used when
// aiming to minimize compile times.
GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP)
-GTNODE(LEA , GenTreeAddrMode ,0,GTK_BINOP|GTK_EXOP)
+GTNODE(LEA , GenTreeAddrMode ,0,(GTK_BINOP|GTK_EXOP))
#if !defined(_TARGET_64BIT_)
// A GT_LONG node simply represents the long value produced by the concatenation
@@ -200,35 +200,35 @@ GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP)
#endif // !defined(_TARGET_64BIT_)
#ifdef FEATURE_SIMD
-GTNODE(SIMD , GenTreeSIMD ,0,GTK_BINOP|GTK_EXOP) // SIMD functions/operators/intrinsics
+GTNODE(SIMD , GenTreeSIMD ,0,(GTK_BINOP|GTK_EXOP)) // SIMD functions/operators/intrinsics
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HWIntrinsic , GenTreeHWIntrinsic ,0,GTK_BINOP|GTK_EXOP) // hardware intrinsics
+GTNODE(HWIntrinsic , GenTreeHWIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // hardware intrinsics
#endif // FEATURE_HW_INTRINSICS
//-----------------------------------------------------------------------------
// LIR specific compare and conditional branch/set nodes:
//-----------------------------------------------------------------------------
-GTNODE(CMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // Sets the condition flags according to the compare result.
+GTNODE(CMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Sets the condition flags according to the compare result.
// N.B. Not a relop, it does not produce a value and it cannot be reversed.
-GTNODE(JCMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // Makes a comparison and jump if the condition specified. Does not set flags
-GTNODE(JCC , GenTreeCC ,0,GTK_LEAF|GTK_NOVALUE) // Checks the condition flags and branch if the condition specified
+GTNODE(JCMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Makes a comparison and jump if the condition specified. Does not set flags
+GTNODE(JCC , GenTreeCC ,0,(GTK_LEAF|GTK_NOVALUE)) // Checks the condition flags and branch if the condition specified
// by GenTreeCC::gtCondition is true.
GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF) // Checks the condition flags and produces 1 if the condition specified
// by GenTreeCC::gtCondition is true and 0 otherwise.
#ifdef _TARGET_XARCH_
-GTNODE(BT , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // The XARCH BT instruction. Like CMP, this sets the condition flags (CF
+GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // The XARCH BT instruction. Like CMP, this sets the condition flags (CF
// to be precise) and does not produce a value.
#endif
//-----------------------------------------------------------------------------
// Other nodes that look like unary/binary operators:
//-----------------------------------------------------------------------------
-GTNODE(JTRUE , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE)
+GTNODE(JTRUE , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE))
-GTNODE(LIST , GenTreeArgList ,0,GTK_BINOP|GTK_NOVALUE)
+GTNODE(LIST , GenTreeArgList ,0,(GTK_BINOP|GTK_NOVALUE))
GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_BINOP) // List of fields of a struct, when passed as an argument
//-----------------------------------------------------------------------------
@@ -237,31 +237,31 @@ GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_BINOP) // List of fields of
GTNODE(FIELD , GenTreeField ,0,GTK_SPECIAL) // Member-field
GTNODE(ARR_ELEM , GenTreeArrElem ,0,GTK_SPECIAL) // Multi-dimensional array-element address
-GTNODE(ARR_INDEX , GenTreeArrIndex ,0,GTK_BINOP|GTK_EXOP) // Effective, bounds-checked index for one dimension of a multi-dimensional array element
+GTNODE(ARR_INDEX , GenTreeArrIndex ,0,(GTK_BINOP|GTK_EXOP)) // Effective, bounds-checked index for one dimension of a multi-dimensional array element
GTNODE(ARR_OFFSET , GenTreeArrOffs ,0,GTK_SPECIAL) // Flattened offset of multi-dimensional array element
-GTNODE(CALL , GenTreeCall ,0,GTK_SPECIAL|GTK_NOCONTAIN)
+GTNODE(CALL , GenTreeCall ,0,(GTK_SPECIAL|GTK_NOCONTAIN))
//-----------------------------------------------------------------------------
// Statement operator nodes:
//-----------------------------------------------------------------------------
-GTNODE(BEG_STMTS , GenTree ,0,GTK_SPECIAL|GTK_NOVALUE)// used only temporarily in importer by impBegin/EndTreeList()
-GTNODE(STMT , GenTreeStmt ,0,GTK_SPECIAL|GTK_NOVALUE)// top-level list nodes in bbTreeList
+GTNODE(BEG_STMTS , GenTree ,0,(GTK_SPECIAL|GTK_NOVALUE))// used only temporarily in importer by impBegin/EndTreeList()
+GTNODE(STMT , GenTreeStmt ,0,(GTK_SPECIAL|GTK_NOVALUE))// top-level list nodes in bbTreeList
-GTNODE(RETURN , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // return from current function
-GTNODE(SWITCH , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // switch
+GTNODE(RETURN , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // return from current function
+GTNODE(SWITCH , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // switch
-GTNODE(NO_OP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // nop!
+GTNODE(NO_OP , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // nop!
-GTNODE(START_NONGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // starts a new instruction group that will be non-gc interruptible
+GTNODE(START_NONGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group that will be non-gc interruptible
-GTNODE(START_PREEMPTGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // starts a new instruction group where preemptive GC is enabled
+GTNODE(START_PREEMPTGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group where preemptive GC is enabled
-GTNODE(PROF_HOOK , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // profiler Enter/Leave/TailCall hook
+GTNODE(PROF_HOOK , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // profiler Enter/Leave/TailCall hook
-GTNODE(RETFILT , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // end filter with TYP_I_IMPL return value
+GTNODE(RETFILT , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // end filter with TYP_I_IMPL return value
#if !FEATURE_EH_FUNCLETS
-GTNODE(END_LFIN , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // end locally-invoked finally
+GTNODE(END_LFIN , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // end locally-invoked finally
#endif // !FEATURE_EH_FUNCLETS
//-----------------------------------------------------------------------------
@@ -269,14 +269,14 @@ GTNODE(END_LFIN , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // end l
//-----------------------------------------------------------------------------
GTNODE(PHI , GenTreeOp ,0,GTK_UNOP) // phi node for ssa.
-GTNODE(PHI_ARG , GenTreePhiArg ,0,GTK_LEAF|GTK_LOCAL) // phi(phiarg, phiarg, phiarg)
+GTNODE(PHI_ARG , GenTreePhiArg ,0,(GTK_LEAF|GTK_LOCAL)) // phi(phiarg, phiarg, phiarg)
//-----------------------------------------------------------------------------
// Nodes used by Lower to generate a closer CPU representation of other nodes
//-----------------------------------------------------------------------------
-GTNODE(JMPTABLE , GenTreeJumpTable ,0, GTK_LEAF|GTK_NOCONTAIN) // Generates the jump table for switches
-GTNODE(SWITCH_TABLE , GenTreeOp ,0, GTK_BINOP|GTK_NOVALUE) // Jump Table based switch construct
+GTNODE(JMPTABLE , GenTreeJumpTable ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Generates the jump table for switches
+GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct
//-----------------------------------------------------------------------------
// Nodes used only within the code generator:
diff --git a/src/jit/hwintrinsicArm64.cpp b/src/jit/hwintrinsicArm64.cpp
index 31df5b0456..b662a43629 100644
--- a/src/jit/hwintrinsicArm64.cpp
+++ b/src/jit/hwintrinsicArm64.cpp
@@ -11,7 +11,7 @@ namespace IsaFlag
{
enum Flag
{
-#define HARDWARE_INTRINSIC_CLASS(flag, isa) isa = 1ULL << InstructionSet_##isa,
+#define HARDWARE_INTRINSIC_CLASS(flag, jit_config, isa) isa = 1ULL << InstructionSet_##isa,
#include "hwintrinsiclistArm64.h"
None = 0,
Base = 1ULL << InstructionSet_Base,
@@ -77,7 +77,7 @@ InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
{
if (strcmp(className, "Base") == 0)
return InstructionSet_Base;
-#define HARDWARE_INTRINSIC_CLASS(flag, isa) \
+#define HARDWARE_INTRINSIC_CLASS(flag, jit_config, isa) \
if (strcmp(className, #isa) == 0) \
return InstructionSet_##isa;
#include "hwintrinsiclistArm64.h"
@@ -139,7 +139,7 @@ NamedIntrinsic Compiler::lookupHWIntrinsic(const char* className, const char* me
//
// Notes:
// This currently returns true for all partially-implemented ISAs.
-// TODO-Bug: Set this to return the correct values as GH 20427 is resolved.
+// TODO-Bug: Set this to return the correct values as https://github.com/dotnet/coreclr/issues/20427 is resolved.
//
bool HWIntrinsicInfo::isFullyImplementedIsa(InstructionSet isa)
{
diff --git a/src/jit/hwintrinsiclistArm64.h b/src/jit/hwintrinsiclistArm64.h
index 3a2ae3afab..cbf60c7374 100644
--- a/src/jit/hwintrinsiclistArm64.h
+++ b/src/jit/hwintrinsiclistArm64.h
@@ -11,27 +11,27 @@
// clang-format off
#if defined(HARDWARE_INTRINSIC_CLASS)
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_AES , Aes )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_ATOMICS , Atomics )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_CRC32 , Crc32 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_DCPOP , Dcpop )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_DP , Dp )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FCMA , Fcma )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FP , Fp )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FP16 , Fp16 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_JSCVT , Jscvt )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_LRCPC , Lrcpc )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_PMULL , Pmull )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA1 , Sha1 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA256 , Sha256 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA512 , Sha512 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA3 , Sha3 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD , Simd )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD_V81 , Simd_v81 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD_FP16 , Simd_fp16)
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SM3 , Sm3 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SM4 , Sm4 )
-HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SVE , Sve )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_AES , EnableArm64Aes , Aes )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_ATOMICS , EnableArm64Atomics , Atomics )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_CRC32 , EnableArm64Crc32 , Crc32 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_DCPOP , EnableArm64Dcpop , Dcpop )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_DP , EnableArm64Dp , Dp )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FCMA , EnableArm64Fcma , Fcma )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FP , EnableArm64Fp , Fp )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_FP16 , EnableArm64Fp16 , Fp16 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_JSCVT , EnableArm64Jscvt , Jscvt )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_LRCPC , EnableArm64Lrcpc , Lrcpc )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_PMULL , EnableArm64Pmull , Pmull )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA1 , EnableArm64Sha1 , Sha1 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA256 , EnableArm64Sha256 , Sha256 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA512 , EnableArm64Sha512 , Sha512 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA3 , EnableArm64Sha3 , Sha3 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD , EnableArm64Simd , Simd )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD_V81 , EnableArm64Simd_v81 , Simd_v81 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD_FP16 , EnableArm64Simd_fp16, Simd_fp16)
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SM3 , EnableArm64Sm3 , Sm3 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SM4 , EnableArm64Sm4 , Sm4 )
+HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SVE , EnableArm64Sve , Sve )
#endif // defined(HARDWARE_INTRINSIC_CLASS)
#if defined(HARDWARE_INTRINSIC)
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 9c2236d5a7..ab99a5c294 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -108,10 +108,10 @@ void Compiler::impPushOnStack(GenTree* tree, typeInfo ti)
// attempts to do that have proved too difficult. Instead, we'll assume that in checks like this,
// when there's a mismatch, it's because of this reason -- the typeInfo::AreEquivalentModuloNativeInt
// method used in the last disjunct allows exactly this mismatch.
- assert(ti.IsDead() || ti.IsByRef() && (tree->TypeGet() == TYP_I_IMPL || tree->TypeGet() == TYP_BYREF) ||
- ti.IsUnboxedGenericTypeVar() && tree->TypeGet() == TYP_REF ||
- ti.IsObjRef() && tree->TypeGet() == TYP_REF || ti.IsMethod() && tree->TypeGet() == TYP_I_IMPL ||
- ti.IsType(TI_STRUCT) && tree->TypeGet() != TYP_REF ||
+ assert(ti.IsDead() || (ti.IsByRef() && (tree->TypeGet() == TYP_I_IMPL) || tree->TypeGet() == TYP_BYREF) ||
+ (ti.IsUnboxedGenericTypeVar() && tree->TypeGet() == TYP_REF) ||
+ (ti.IsObjRef() && tree->TypeGet() == TYP_REF) || (ti.IsMethod() && tree->TypeGet() == TYP_I_IMPL) ||
+ (ti.IsType(TI_STRUCT) && tree->TypeGet() != TYP_REF) ||
typeInfo::AreEquivalentModuloNativeInt(NormaliseForStack(ti),
NormaliseForStack(typeInfo(tree->TypeGet()))));
@@ -4143,7 +4143,7 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
// Intrinsics that are not implemented directly by target instructions will
// be re-materialized as users calls in rationalizer. For prefixed tail calls,
// don't do this optimization, because
- // a) For back compatibility reasons on desktop.Net 4.6 / 4.6.1
+ // a) For back compatibility reasons on desktop .NET Framework 4.6 / 4.6.1
// b) It will be non-trivial task or too late to re-materialize a surviving
// tail prefixed GT_INTRINSIC as tail call in rationalizer.
if (!IsIntrinsicImplementedByUserCall(intrinsicID) || !tailCall)
@@ -10139,10 +10139,10 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
// to have a tree like this:
//
// -
- // / \
- // / \
- // / \
- // / \
+ // / \.
+ // / \.
+ // / \.
+ // / \.
// const(h) int addr byref
//
// <BUGNUM> VSW 318822 </BUGNUM>
@@ -10450,7 +10450,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// expand the methodtable match:
//
// condMT ==> GT_NE
- // / \
+ // / \.
// GT_IND op2 (typically CNS_INT)
// |
// op1Copy
@@ -10479,7 +10479,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// expand the null check:
//
// condNull ==> GT_EQ
- // / \
+ // / \.
// op1Copy CNS_INT
// null
//
@@ -10512,9 +10512,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// Generate first QMARK - COLON tree
//
// qmarkMT ==> GT_QMARK
- // / \
+ // / \.
// condMT GT_COLON
- // / \
+ // / \.
// condFalse condTrue
//
temp = new (this, GT_COLON) GenTreeColon(TYP_REF, condTrue, condFalse);
@@ -10525,9 +10525,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// Generate second QMARK - COLON tree
//
// qmarkNull ==> GT_QMARK
- // / \
+ // / \.
// condNull GT_COLON
- // / \
+ // / \.
// qmarkMT op1Copy
//
temp = new (this, GT_COLON) GenTreeColon(TYP_REF, gtClone(op1), qmarkMT);
@@ -18169,8 +18169,8 @@ BOOL Compiler::impIsAddressInLocal(GenTree* tree, GenTree** lclVarTreeOut)
void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, InlineResult* inlineResult)
{
- assert(pInlineInfo != nullptr && compIsForInlining() || // Perform the actual inlining.
- pInlineInfo == nullptr && !compIsForInlining() // Calculate the static inlining hint for ngen.
+ assert((pInlineInfo != nullptr && compIsForInlining()) || // Perform the actual inlining.
+ (pInlineInfo == nullptr && !compIsForInlining()) // Calculate the static inlining hint for ngen.
);
// If we're really inlining, we should just have one result in play.
diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h
index 842d729111..998848ec90 100644
--- a/src/jit/jitconfigvalues.h
+++ b/src/jit/jitconfigvalues.h
@@ -253,6 +253,35 @@ CONFIG_INTEGER(EnablePOPCNT, W("EnablePOPCNT"), 1) // Enable POPCNT
// Enable AVX instruction set for wide operations as default
CONFIG_INTEGER(EnableAVX, W("EnableAVX"), 0)
#endif // !defined(_TARGET_AMD64_) && !defined(_TARGET_X86_)
+
+// clang-format off
+
+#if defined(_TARGET_ARM64_)
+CONFIG_INTEGER(EnableArm64Aes , W("EnableArm64Aes"), 1)
+CONFIG_INTEGER(EnableArm64Atomics , W("EnableArm64Atomics"), 1)
+CONFIG_INTEGER(EnableArm64Crc32 , W("EnableArm64Crc32"), 1)
+CONFIG_INTEGER(EnableArm64Dcpop , W("EnableArm64Dcpop"), 1)
+CONFIG_INTEGER(EnableArm64Dp , W("EnableArm64Dp"), 1)
+CONFIG_INTEGER(EnableArm64Fcma , W("EnableArm64Fcma"), 1)
+CONFIG_INTEGER(EnableArm64Fp , W("EnableArm64Fp"), 1)
+CONFIG_INTEGER(EnableArm64Fp16 , W("EnableArm64Fp16"), 1)
+CONFIG_INTEGER(EnableArm64Jscvt , W("EnableArm64Jscvt"), 1)
+CONFIG_INTEGER(EnableArm64Lrcpc , W("EnableArm64Lrcpc"), 1)
+CONFIG_INTEGER(EnableArm64Pmull , W("EnableArm64Pmull"), 1)
+CONFIG_INTEGER(EnableArm64Sha1 , W("EnableArm64Sha1"), 1)
+CONFIG_INTEGER(EnableArm64Sha256 , W("EnableArm64Sha256"), 1)
+CONFIG_INTEGER(EnableArm64Sha512 , W("EnableArm64Sha512"), 1)
+CONFIG_INTEGER(EnableArm64Sha3 , W("EnableArm64Sha3"), 1)
+CONFIG_INTEGER(EnableArm64Simd , W("EnableArm64Simd"), 1)
+CONFIG_INTEGER(EnableArm64Simd_v81 , W("EnableArm64Simd_v81"), 1)
+CONFIG_INTEGER(EnableArm64Simd_fp16, W("EnableArm64Simd_fp16"), 1)
+CONFIG_INTEGER(EnableArm64Sm3 , W("EnableArm64Sm3"), 1)
+CONFIG_INTEGER(EnableArm64Sm4 , W("EnableArm64Sm4"), 1)
+CONFIG_INTEGER(EnableArm64Sve , W("EnableArm64Sve"), 1)
+#endif // defined(_TARGET_ARM64_)
+
+// clang-format on
+
///
/// JIT
///
diff --git a/src/jit/jitstd/hashtable.h b/src/jit/jitstd/hashtable.h
index 05b033a746..27b47107aa 100644
--- a/src/jit/jitstd/hashtable.h
+++ b/src/jit/jitstd/hashtable.h
@@ -777,6 +777,7 @@ typename hashtable<Key, Value, Hash, Pred, Alloc, KeyOf>::size_type
hashtable<Key, Value, Hash, Pred, Alloc, KeyOf>::bucket_size(size_type size) const
{
rehash(size);
+ return bucket_count();
}
template <typename Key, typename Value, typename Hash, typename Pred, typename Alloc, typename KeyOf>
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index a251534bd1..d4916cbf86 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -1351,7 +1351,7 @@ unsigned Compiler::compMapILvarNum(unsigned ILvarNum)
* Returns UNKNOWN_ILNUM if it can't be mapped.
*/
-unsigned Compiler::compMap2ILvarNum(unsigned varNum)
+unsigned Compiler::compMap2ILvarNum(unsigned varNum) const
{
if (compIsForInlining())
{
@@ -7290,7 +7290,7 @@ int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
return lvaToCallerSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
}
-int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased)
+int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased) const
{
assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
diff --git a/src/jit/loopcloning.h b/src/jit/loopcloning.h
index cd9aa9f946..d0ec6b6b98 100644
--- a/src/jit/loopcloning.h
+++ b/src/jit/loopcloning.h
@@ -113,7 +113,7 @@ struct ArrIndex
void Print(unsigned dim = -1)
{
printf("V%02d", arrLcl);
- for (unsigned i = 0; i < ((dim == -1) ? rank : dim); ++i)
+ for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i)
{
printf("[V%02d]", indLcls.GetRef(i));
}
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 9d0193ba85..fb6b3f4417 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -4322,7 +4322,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
// call fgMorphMultiregStructArg on each of them.
//
// Arguments:
-// call : a GenTreeCall node that has one or more TYP_STRUCT arguments\
+// call : a GenTreeCall node that has one or more TYP_STRUCT arguments\.
//
// Notes:
// We only call fgMorphMultiregStructArg for struct arguments that are not passed as simple types.
@@ -6447,7 +6447,7 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// Build this tree: IND(*) #
// |
// ADD(I_IMPL)
- // / \
+ // / \.
// / CNS(fldOffset)
// /
// /
@@ -6455,9 +6455,9 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// IND(I_IMPL) == [Base of this DLL's TLS]
// |
// ADD(I_IMPL)
- // / \
+ // / \.
// / CNS(IdValue*4) or MUL
- // / / \
+ // / / \.
// IND(I_IMPL) / CNS(4)
// | /
// CNS(TLS_HDL,0x2C) IND
@@ -7372,6 +7372,12 @@ void Compiler::fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs)
if (call->IsVirtualStub())
{
GenTree* stubAddrArg = fgGetStubAddrArg(call);
+
+ // We don't need this arg to be in the normal stub register, so
+ // clear out the register assignment.
+ assert(stubAddrArg->gtRegNum == virtualStubParamInfo->GetReg());
+ stubAddrArg->gtRegNum = REG_NA;
+
// And push the stub address onto the list of arguments
call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs);
}
@@ -7606,6 +7612,12 @@ void Compiler::fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs)
if (call->IsVirtualStub())
{
GenTree* stubAddrArg = fgGetStubAddrArg(call);
+
+ // We don't need this arg to be in the normal stub register, so
+ // clear out the register assignment.
+ assert(stubAddrArg->gtRegNum == virtualStubParamInfo->GetReg());
+ stubAddrArg->gtRegNum = REG_NA;
+
// And push the stub address onto the list of arguments
call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs);
}
@@ -9048,7 +9060,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
// See if we can do a simple transformation:
//
// GT_ASG <TYP_size>
- // / \
+ // / \.
// GT_IND GT_IND or CNS_INT
// | |
// [dest] [src]
@@ -12408,8 +12420,8 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following tree
//
// EQ/NE
- // / \
- // op1 CNS 0/1
+ // / \.
+ // op1 CNS 0/1
//
ival2 = INT_MAX; // The value of INT_MAX for ival2 just means that the constant value is not 0 or 1
@@ -12433,12 +12445,12 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following transformation
//
// EQ/NE Possible REVERSE(RELOP)
- // / \ / \
- // COMMA CNS 0/1 -> COMMA relop_op2
- // / \ / \
- // x RELOP x relop_op1
- // / \
- // relop_op1 relop_op2
+ // / \ / \.
+ // COMMA CNS 0/1 -> COMMA relop_op2
+ // / \ / \.
+ // x RELOP x relop_op1
+ // / \.
+ // relop_op1 relop_op2
//
//
//
@@ -12476,14 +12488,14 @@ DONE_MORPHING_CHILDREN:
// and when the LCL_VAR is a temp we can fold the tree:
//
// EQ/NE EQ/NE
- // / \ / \
- // COMMA CNS 0/1 -> RELOP CNS 0/1
- // / \ / \
- // ASG LCL_VAR
- // / \
- // LCL_VAR RELOP
- // / \
- //
+ // / \ / \.
+ // COMMA CNS 0/1 -> RELOP CNS 0/1
+ // / \ / \.
+ // ASG LCL_VAR
+ // / \.
+ // LCL_VAR RELOP
+ // / \.
+ //
GenTree* asg = op1->gtOp.gtOp1;
GenTree* lcl = op1->gtOp.gtOp2;
@@ -12550,9 +12562,9 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following tree
//
// EQ/NE -> RELOP/!RELOP
- // / \ / \
+ // / \ / \.
// RELOP CNS 0/1
- // / \
+ // / \.
//
// Note that we will remove/destroy the EQ/NE node and move
// the RELOP up into it's location.
@@ -12582,12 +12594,12 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following transformation:
//
// EQ/NE EQ/NE
- // / \ / \
- // AND CNS 0/1 -> AND CNS 0
- // / \ / \
- // RSZ/RSH CNS 1 x CNS (1 << y)
- // / \
- // x CNS_INT +y
+ // / \ / \.
+ // AND CNS 0/1 -> AND CNS 0
+ // / \ / \.
+ // RSZ/RSH CNS 1 x CNS (1 << y)
+ // / \.
+ // x CNS_INT +y
if (op1->gtOper == GT_AND)
{
@@ -14008,12 +14020,12 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree)
{
// This takes
// + (tree)
- // / \
- // / \
- // / \
+ // / \.
+ // / \.
+ // / \.
// + (op1) op2
- // / \
- // \
+ // / \.
+ // \.
// ad2
//
// And it swaps ad2 and op2. If (op2) is varTypeIsGC, then this implies that (tree) is
@@ -14348,13 +14360,13 @@ GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree)
// Check for a rotation pattern, e.g.,
//
// OR ROL
- // / \ / \
+ // / \ / \.
// LSH RSZ -> x y
- // / \ / \
+ // / \ / \.
// x AND x AND
- // / \ / \
+ // / \ / \.
// y 31 ADD 31
- // / \
+ // / \.
// NEG 32
// |
// y
diff --git a/src/jit/regset.h b/src/jit/regset.h
index 469178dce4..3e866be169 100644
--- a/src/jit/regset.h
+++ b/src/jit/regset.h
@@ -44,7 +44,7 @@ public:
RegSet(Compiler* compiler, GCInfo& gcInfo);
#ifdef _TARGET_ARM_
- regMaskTP rsMaskPreSpillRegs(bool includeAlignment)
+ regMaskTP rsMaskPreSpillRegs(bool includeAlignment) const
{
return includeAlignment ? (rsMaskPreSpillRegArg | rsMaskPreSpillAlign) : rsMaskPreSpillRegArg;
}
diff --git a/src/jit/scopeinfo.cpp b/src/jit/scopeinfo.cpp
index 19aa5428fa..3a47a32453 100644
--- a/src/jit/scopeinfo.cpp
+++ b/src/jit/scopeinfo.cpp
@@ -58,7 +58,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "emit.h"
#include "codegen.h"
-bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg)
+bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg) const
{
switch (vlType)
{
@@ -82,7 +82,7 @@ bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg)
}
}
-bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset)
+bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset) const
{
regNumber actualReg;
@@ -155,6 +155,79 @@ CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg,
}
//------------------------------------------------------------------------
+// Equals: Compares first reference and then values of the structures.
+//
+// Arguments:
+// lhs - a "siVarLoc *" to compare.
+// rhs - a "siVarLoc *" to compare.
+//
+// Notes:
+// Return true if both are nullptr.
+//
+// static
+bool CodeGenInterface::siVarLoc::Equals(const siVarLoc* lhs, const siVarLoc* rhs)
+{
+ if (lhs == rhs)
+ {
+ // Are both nullptr or the same reference
+ return true;
+ }
+ if ((lhs == nullptr) || (rhs == nullptr))
+ {
+ // Just one of them is a nullptr
+ return false;
+ }
+ if (lhs->vlType != rhs->vlType)
+ {
+ return false;
+ }
+ assert(lhs->vlType == rhs->vlType);
+ // If neither is nullptr, and are not the same reference, compare values
+ switch (lhs->vlType)
+ {
+ case VLT_STK:
+ case VLT_STK_BYREF:
+ return (lhs->vlStk.vlsBaseReg == rhs->vlStk.vlsBaseReg) && (lhs->vlStk.vlsOffset == rhs->vlStk.vlsOffset);
+
+ case VLT_STK2:
+ return (lhs->vlStk2.vls2BaseReg == rhs->vlStk2.vls2BaseReg) &&
+ (lhs->vlStk2.vls2Offset == rhs->vlStk2.vls2Offset);
+
+ case VLT_REG:
+ case VLT_REG_FP:
+ case VLT_REG_BYREF:
+ return (lhs->vlReg.vlrReg == rhs->vlReg.vlrReg);
+
+ case VLT_REG_REG:
+ return (lhs->vlRegReg.vlrrReg1 == rhs->vlRegReg.vlrrReg1) &&
+ (lhs->vlRegReg.vlrrReg2 == rhs->vlRegReg.vlrrReg2);
+
+ case VLT_REG_STK:
+ return (lhs->vlRegStk.vlrsReg == rhs->vlRegStk.vlrsReg) &&
+ (lhs->vlRegStk.vlrsStk.vlrssBaseReg == rhs->vlRegStk.vlrsStk.vlrssBaseReg) &&
+ (lhs->vlRegStk.vlrsStk.vlrssOffset == rhs->vlRegStk.vlrsStk.vlrssOffset);
+
+ case VLT_STK_REG:
+ return (lhs->vlStkReg.vlsrReg == rhs->vlStkReg.vlsrReg) &&
+ (lhs->vlStkReg.vlsrStk.vlsrsBaseReg == rhs->vlStkReg.vlsrStk.vlsrsBaseReg) &&
+ (lhs->vlStkReg.vlsrStk.vlsrsOffset == rhs->vlStkReg.vlsrStk.vlsrsOffset);
+
+ case VLT_FPSTK:
+ return (lhs->vlFPstk.vlfReg == rhs->vlFPstk.vlfReg);
+
+ case VLT_FIXED_VA:
+ return (lhs->vlFixedVarArg.vlfvOffset == rhs->vlFixedVarArg.vlfvOffset);
+
+ case VLT_COUNT:
+ case VLT_INVALID:
+ return true;
+
+ default:
+ unreached();
+ }
+}
+
+//------------------------------------------------------------------------
// getSiVarLoc: Creates a "CodegenInterface::siVarLoc" instance from using the properties
// of the "psiScope" instance.
//
diff --git a/src/jit/unwindarm.cpp b/src/jit/unwindarm.cpp
index 4a2b838258..936ba1b656 100644
--- a/src/jit/unwindarm.cpp
+++ b/src/jit/unwindarm.cpp
@@ -1780,7 +1780,7 @@ void UnwindInfo::InitUnwindInfo(Compiler* comp, emitLocation* startLoc, emitLoca
// However, its constructor needs to be explicitly called, since the constructor for
// UnwindInfo is not called.
- uwiFragmentFirst.UnwindFragmentInfo::UnwindFragmentInfo(comp, startLoc, false);
+ new (&uwiFragmentFirst, jitstd::placement_t()) UnwindFragmentInfo(comp, startLoc, false);
uwiFragmentLast = &uwiFragmentFirst;
diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp
index 2010678242..888fe605b0 100644
--- a/src/jit/utils.cpp
+++ b/src/jit/utils.cpp
@@ -2153,9 +2153,9 @@ const SignedMagic<int32_t>* TryGetSignedMagic(int32_t divisor)
static const SignedMagic<int32_t> table[]{
{0x55555556, 0}, // 3
{},
- {0x66666667, 1}, // 5
- {0x2aaaaaab, 0}, // 6
- {0x92492493, 2}, // 7
+ {0x66666667, 1}, // 5
+ {0x2aaaaaab, 0}, // 6
+ {(int32_t)0x92492493, 2}, // 7
{},
{0x38e38e39, 1}, // 9
{0x66666667, 2}, // 10
diff --git a/src/nativeresources/rctocpp.awk b/src/nativeresources/rctocpp.awk
index 6f8d597cf4..cef49ce4a5 100644
--- a/src/nativeresources/rctocpp.awk
+++ b/src/nativeresources/rctocpp.awk
@@ -62,7 +62,7 @@ function writeheader(arrayName, tableName)
print "// This code was generated by rctocpp.awk and is not meant to be modified manually."
print "#include <resourcestring.h>";
print "";
- print "extern const NativeStringResourceTable " tableName ";";
+ print "extern NativeStringResourceTable " tableName ";";
print "const NativeStringResource " arrayName "[] = {";
}
@@ -78,7 +78,7 @@ function writefooter(arrayName, tableName)
print "};";
print "";
- print "const NativeStringResourceTable " tableName " __attribute__((visibility(\"default\"))) = {";
+ print "NativeStringResourceTable " tableName " __attribute__((visibility(\"default\"))) = {";
print numEntries ",";
print arrayName "};";
}
diff --git a/src/pal/automation/automation.py b/src/pal/automation/automation.py
deleted file mode 100644
index d8ddaa9117..0000000000
--- a/src/pal/automation/automation.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import logging as log
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import compile
-import tests
-import util
-
-target = ""
-arch = ""
-platform = ""
-workspace = ""
-fullbuilddirpath = ""
-cleanUp = True
-
-def ParseArgs(argv):
- global target
- global platform
- global arch
- global cleanUp
-
- returncode,target,platform,arch,cleanUp = util.ParseArgs(argv)
- return returncode
-
-def Initialize():
- global workspace
- workspace = util.Initialize(platform)
- return 0
-
-def SetupDirectories():
- global fullbuilddirpath
- fullbuilddirpath = util.SetupDirectories(target, arch, platform)
-
- return 0
-
-def CopyBinaries():
- print "\n==================================================\n"
- print "Stub for copying binaries"
- print "\n==================================================\n"
-
- return 0
-
-def main(argv):
- returncode = ParseArgs(argv)
- if returncode != 0:
- return returncode
-
- returncode += Initialize()
- if returncode != 0:
- return returncode
-
- returncode += SetupDirectories()
- if returncode != 0:
- return returncode
-
- returncode += compile.Compile(workspace, target, platform, arch)
- if returncode != 0:
- return returncode
-
- returncode += CopyBinaries()
- if returncode != 0:
- return returncode
-
- returncode += tests.RunTests(platform, fullbuilddirpath, workspace)
- if returncode != 0:
- return returncode
-
- return returncode
-
-if __name__ == "__main__":
- returncode = main(sys.argv[1:])
-
- util.Cleanup(cleanUp,workspace)
-
- sys.exit(returncode)
-
diff --git a/src/pal/automation/compile.py b/src/pal/automation/compile.py
deleted file mode 100644
index bb9d1b88ac..0000000000
--- a/src/pal/automation/compile.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import logging as log
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-
-def RunCMake(workspace, target, platform):
- # run CMake
- print "\n==================================================\n"
-
- returncode = 0
- if platform == "windows":
- print "Running: vcvarsall.bat x86_amd64 && " + workspace + "\ProjectK\NDP\clr\src\pal\\tools\gen-buildsys-win.bat " + workspace + "\ProjectK\NDP\clr"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(["vcvarsall.bat", "x86_amd64", "&&", workspace + "\ProjectK\NDP\clr\src\pal\\tools\gen-buildsys-win.bat", workspace + "\ProjectK\NDP\clr"])
- elif platform == "linux":
- print "Running: " + workspace + "/ProjectK/NDP/clr/src/pal/tools/gen-buildsys-clang.sh " + workspace + "/ProjectK/NDP/clr DEBUG"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(workspace + "/ProjectK/NDP/clr/src/pal/tools/gen-buildsys-clang.sh " + workspace + "/ProjectK/NDP/clr " + target, shell=True)
-
- if returncode != 0:
- print "ERROR: cmake failed with exit code " + str(returncode)
-
- return returncode
-
-def RunBuild(target, platform, arch):
- if platform == "windows":
- return RunMsBuild(target, arch)
- elif platform == "linux":
- return RunMake()
-
-def RunMsBuild(target, arch):
- # run MsBuild
- print "\n==================================================\n"
- print "Running: vcvarsall.bat x86_amd64 && msbuild CoreCLR.sln /p:Configuration=" + target + " /p:Platform=" + arch
- print "\n==================================================\n"
- sys.stdout.flush()
-
- returncode = subprocess.call(["vcvarsall.bat","x86_amd64","&&","msbuild","CoreCLR.sln","/p:Configuration=" + target,"/p:Platform=" + arch])
-
- if returncode != 0:
- print "ERROR: vcvarsall.bat failed with exit code " + str(returncode)
-
- return returncode
-
-def RunMake():
- print "\n==================================================\n"
- print "Running: make"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(["make"])
-
- if returncode != 0:
- print "ERROR: make failed with exit code " + str(returncode)
-
- return returncode
-
-def Compile(workspace, target, platform, arch):
- returncode = RunCMake(workspace, target, platform)
- if returncode != 0:
- return returncode
-
- returncode += RunBuild(target, platform, arch)
- if returncode != 0:
- return returncode
-
- return returncode
diff --git a/src/pal/automation/tests.py b/src/pal/automation/tests.py
deleted file mode 100644
index e48e84fd1d..0000000000
--- a/src/pal/automation/tests.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import logging as log
-
-def RunPalTests(fullbuilddirpath, workspace):
- print "\n==================================================\n"
- print "Running PAL Tests."
- print "\n==================================================\n"
-
- print "Running: " + workspace + "/ProjectK/NDP/clr/src/pal/tests/palsuite/runpaltests.sh " + fullbuilddirpath + " " + fullbuilddirpath + "/PalTestOutput"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(workspace + "/ProjectK/NDP/clr/src/pal/tests/palsuite/runpaltests.sh " + fullbuilddirpath + " " + fullbuilddirpath + "/PalTestOutput", shell=True)
-
- if returncode != 0:
- print "ERROR: there were errors failed with exit code " + str(returncode)
-
- return returncode
-
-
-def RunTests(platform, fullbuilddirpath, workspace):
- returncode = 0
-
- if platform == "linux":
- # Execute PAL tests
- returncode = RunPalTests(fullbuilddirpath, workspace)
-
- return returncode
-
-
diff --git a/src/pal/automation/util.py b/src/pal/automation/util.py
deleted file mode 100644
index 790cbc4dbf..0000000000
--- a/src/pal/automation/util.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import logging as log
-
-def Initialize(platform):
- print "Initializing Workspace"
- global workspace
- workspace = os.environ['WORKSPACE']
- if platform == "windows":
- # Jenkins puts quotes in the path, which is wrong. Remove quotes.
- os.environ['PATH'] = os.environ['PATH'].replace('"','')
-
- return workspace
-
-def ParseArgs(argv):
- print "Parsing arguments for compile"
- try:
- opts, args = getopt.getopt(argv, "t:p:a:v", ["target=", "platform=", "arch=", "verbose","noclean"])
- except getopt.GetoptError:
- print "ERROR: \n\t usage: python compile.py --target <target> --platform <windows|linux> --arch <arch> [--verbose] [--noclean]"
- return 2,"","","",True
-
- verbose = False
- cleanUp = True
-
- acceptedPlatforms = ['windows','linux']
-
- for opt, arg in opts:
- if opt in ("-t", "--target"):
- target = arg
- elif opt in ("-p", "--platform"):
- if arg.lower() not in acceptedPlatforms:
- print "ERROR: " + arg + "not an accepted platform. Use windows or linux."
- sys.exit(2)
- platform = arg.lower()
- elif opt in ("-a", "--arch"):
- arch = arg
- elif opt in ("-v", "--verbose"):
- verbose = True
- elif opt in ("-c", "--noclean"):
- cleanUp = False
-
- if verbose:
- log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
- log.info("In verbose mode.")
- else:
- log.basicConfig(format="%(levelname)s: %(message)s")
-
- if target == "" or platform == "" or arch == "":
- # must specify target, project and arch
- log.error("Must specify target, project and arch")
- return 2,"","","",True
-
- return 0,target,platform,arch,cleanUp
-
-def SetupDirectories(target, arch, platform):
- log.info("Setting up directories")
-
- global rootdir
- global builddir
- global fullBuildDirPath
-
- rootdir = "build"
- if not os.path.isdir(rootdir):
- os.mkdir(rootdir)
- os.chdir(rootdir)
-
- builddir = "build-" + platform
-
- if platform == "windows":
- builddir = builddir + "-" + arch + "-" + target
-
- if os.path.isdir(builddir):
- shutil.rmtree(builddir)
- os.mkdir(builddir)
- os.chdir(builddir)
-
- fullbuilddirpath = workspace + "/" + rootdir + "/" + builddir
-
- return fullbuilddirpath
-
-def Cleanup(cleanUp,workspace):
- print "\n==================================================\n"
- print "Cleaning Up."
- print "\n==================================================\n"
-
- if cleanUp:
- os.chdir(workspace + "/" + rootdir)
- shutil.rmtree(builddir)
- os.chdir("..")
- shutil.rmtree(rootdir)
-
- log.shutdown()
- return 0
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index 26c9e3f6d8..1be000ce8a 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -480,6 +480,17 @@ static const int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH;
PALIMPORT
VOID
PALAPI
+PAL_GetTransportName(
+ const int MAX_TRANSPORT_NAME_LENGTH,
+ OUT char *name,
+ IN const char *prefix,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix);
+
+PALIMPORT
+VOID
+PALAPI
PAL_GetTransportPipeName(
OUT char *name,
IN DWORD id,
diff --git a/src/pal/src/misc/cgroup.cpp b/src/pal/src/misc/cgroup.cpp
index 304b813f28..144ac669df 100644
--- a/src/pal/src/misc/cgroup.cpp
+++ b/src/pal/src/misc/cgroup.cpp
@@ -386,20 +386,18 @@ size_t
PALAPI
PAL_GetRestrictedPhysicalMemoryLimit()
{
- size_t physical_memory_limit;
+ size_t physical_memory_limit = 0;
if (!CGroup::GetPhysicalMemoryLimit(&physical_memory_limit))
return 0;
- else
+
+ // If there's no memory limit specified on the container this
+ // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
+ // 4k which is a common page size). So we know we are not
+ // running in a memory restricted environment.
+ if (physical_memory_limit > 0x7FFFFFFF00000000)
{
- // If there's no memory limit specified on the container this
- // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
- // 4k which is a common page size). So we know we are not
- // running in a memory restricted environment.
- if (physical_memory_limit > 0x7FFFFFFF00000000)
- {
- return 0;
- }
+ return 0;
}
struct rlimit curr_rlimit;
diff --git a/src/pal/src/numa/numa.cpp b/src/pal/src/numa/numa.cpp
index 8793a09b85..9283a044da 100644
--- a/src/pal/src/numa/numa.cpp
+++ b/src/pal/src/numa/numa.cpp
@@ -166,7 +166,7 @@ the processors enabled.
--*/
KAFFINITY GetFullAffinityMask(int cpuCount)
{
- if (cpuCount < sizeof(KAFFINITY) * 8)
+ if ((size_t)cpuCount < sizeof(KAFFINITY) * 8)
{
return ((KAFFINITY)1 << (cpuCount)) - 1;
}
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index 5615036806..f7618cbdb9 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -1549,7 +1549,8 @@ static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
#define HashSemaphoreName(a,b) a,b
#endif
-static const char* PipeNameFormat = "clr-debug-pipe-%d-%llu-%s";
+static const char *const TwoWayNamedPipePrefix = "clr-debug-pipe";
+static const char* IpcNameFormat = "%s-%d-%llu-%s";
class PAL_RuntimeStartupHelper
{
@@ -2288,14 +2289,16 @@ GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey)
/*++
Function:
- PAL_GetTransportPipeName
+ PAL_GetTransportName
- Builds the transport pipe names from the process id.
+ Builds the transport IPC names from the process id.
--*/
VOID
PALAPI
-PAL_GetTransportPipeName(
+PAL_GetTransportName(
+ const int MAX_TRANSPORT_NAME_LENGTH,
OUT char *name,
+ IN const char *prefix,
IN DWORD id,
IN const char *applicationGroupId,
IN const char *suffix)
@@ -2305,7 +2308,7 @@ PAL_GetTransportPipeName(
UINT64 disambiguationKey = 0;
PathCharString formatBufferString;
BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
- char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH-1);
+ char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_TRANSPORT_NAME_LENGTH-1);
if (formatBuffer == nullptr)
{
ERROR("Out Of Memory");
@@ -2337,9 +2340,9 @@ PAL_GetTransportPipeName(
}
// Verify the size of the path won't exceed maximum allowed size
- if (formatBufferString.GetCount() >= MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
+ if (formatBufferString.GetCount() >= MAX_TRANSPORT_NAME_LENGTH)
{
- ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
}
@@ -2347,27 +2350,50 @@ PAL_GetTransportPipeName(
#endif // __APPLE__
{
// Get a temp file location
- dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer);
+ dwRetVal = ::GetTempPathA(MAX_TRANSPORT_NAME_LENGTH, formatBuffer);
if (dwRetVal == 0)
{
ERROR("GetTempPath failed (0x%08x)", ::GetLastError());
return;
}
- if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
+ if (dwRetVal > MAX_TRANSPORT_NAME_LENGTH)
{
- ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("GetTempPath returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
}
- if (strncat_s(formatBuffer, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE)
+ if (strncat_s(formatBuffer, MAX_TRANSPORT_NAME_LENGTH, IpcNameFormat, strlen(IpcNameFormat)) == STRUNCATE)
{
- ERROR("TransportPipeName was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("TransportPipeName was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
- int chars = snprintf(name, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer, id, disambiguationKey, suffix);
- _ASSERTE(chars > 0 && chars < MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH);
+ int chars = snprintf(name, MAX_TRANSPORT_NAME_LENGTH, formatBuffer, prefix, id, disambiguationKey, suffix);
+ _ASSERTE(chars > 0 && chars < MAX_TRANSPORT_NAME_LENGTH);
+}
+
+/*++
+ Function:
+ PAL_GetTransportPipeName
+
+ Builds the transport pipe names from the process id.
+--*/
+VOID
+PALAPI
+PAL_GetTransportPipeName(
+ OUT char *name,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix)
+{
+ PAL_GetTransportName(
+ MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH,
+ name,
+ TwoWayNamedPipePrefix,
+ id,
+ applicationGroupId,
+ suffix);
}
/*++
diff --git a/src/pal/tools/gen-buildsys-clang.sh b/src/pal/tools/gen-buildsys-clang.sh
index 95b227d89f..8f9881ba0c 100755
--- a/src/pal/tools/gen-buildsys-clang.sh
+++ b/src/pal/tools/gen-buildsys-clang.sh
@@ -3,13 +3,14 @@
# This file invokes cmake and generates the build system for Clang.
#
-if [ $# -lt 4 ]
+if [ $# -lt 5 ]
then
echo "Usage..."
- echo "gen-buildsys-clang.sh <path to top level CMakeLists.txt> <ClangMajorVersion> <ClangMinorVersion> <Architecture> [build flavor] [coverage] [ninja] [scan-build] [cmakeargs]"
+ echo "gen-buildsys-clang.sh <path to top level CMakeLists.txt> <ClangMajorVersion> <ClangMinorVersion> <Architecture> <ScriptDirectory> [build flavor] [coverage] [ninja] [scan-build] [cmakeargs]"
echo "Specify the path to the top level CMake file - <ProjectK>/src/NDP"
echo "Specify the clang version to use, split into major and minor version"
- echo "Specify the target architecture."
+ echo "Specify the target architecture."
+ echo "Specify the script directory."
echo "Optionally specify the build configuration (flavor.) Defaults to DEBUG."
echo "Optionally specify 'coverage' to enable code coverage build."
echo "Optionally specify 'scan-build' to enable build with clang static analyzer."
@@ -40,6 +41,7 @@ export CC="$(command -v clang$desired_llvm_version)"
export CXX="$(command -v clang++$desired_llvm_version)"
build_arch="$4"
+script_dir="$5"
buildtype=DEBUG
code_coverage=OFF
build_tests=OFF
@@ -47,7 +49,7 @@ scan_build=OFF
generator="Unix Makefiles"
__UnprocessedCMakeArgs=""
-for i in "${@:5}"; do
+for i in "${@:6}"; do
upperI="$(echo $i | awk '{print toupper($0)}')"
case $upperI in
# Possible build types are DEBUG, CHECKED, RELEASE, RELWITHDEBINFO, MINSIZEREL.
@@ -155,8 +157,7 @@ else
overridefile=clang-compiler-override.txt
fi
-# Determine the current script directory
-__currentScriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+__currentScriptDir="$script_dir"
cmake_command=cmake
diff --git a/src/pal/tools/gen-buildsys-gcc.sh b/src/pal/tools/gen-buildsys-gcc.sh
index 0d66368048..2faf29a887 100755
--- a/src/pal/tools/gen-buildsys-gcc.sh
+++ b/src/pal/tools/gen-buildsys-gcc.sh
@@ -3,13 +3,14 @@
# This file invokes cmake and generates the build system for Gcc.
#
-if [ $# -lt 4 ]
+if [ $# -lt 5 ]
then
echo "Usage..."
- echo "gen-buildsys-gcc.sh <path to top level CMakeLists.txt> <GccMajorVersion> <GccMinorVersion> <Architecture> [build flavor] [coverage] [ninja] [cmakeargs]"
+ echo "gen-buildsys-gcc.sh <path to top level CMakeLists.txt> <GccMajorVersion> <GccMinorVersion> <Architecture> <ScriptDirectory> [build flavor] [coverage] [ninja] [cmakeargs]"
echo "Specify the path to the top level CMake file - <ProjectK>/src/NDP"
echo "Specify the Gcc version to use, split into major and minor version"
echo "Specify the target architecture."
+ echo "Specify the script directory."
echo "Optionally specify the build configuration (flavor.) Defaults to DEBUG."
echo "Optionally specify 'coverage' to enable code coverage build."
echo "Target ninja instead of make. ninja must be on the PATH."
@@ -60,6 +61,7 @@ fi
export CC CXX
build_arch="$4"
+script_dir="$5"
buildtype=DEBUG
code_coverage=OFF
generator="Unix Makefiles"
@@ -68,7 +70,7 @@ __UnprocessedCMakeArgs=""
ITER=-1
for i in "$@"; do
ITER=$((ITER + 1))
- if [ $ITER -lt 5 ]; then continue; fi
+ if [ $ITER -lt 6 ]; then continue; fi
upperI="$(echo "$i" | awk '{print toupper($0)}')"
case $upperI in
# Possible build types are DEBUG, CHECKED, RELEASE, RELWITHDEBINFO, MINSIZEREL.
@@ -158,8 +160,7 @@ fi
overridefile=gcc-compiler-override.txt
-# Determine the current script directory
-__currentScriptDir="$(cd -- "$(dirname -- "$0")" && pwd -P)"
+__currentScriptDir="$script_dir"
cmake \
-G "$generator" \
diff --git a/src/strongname/api/common.h b/src/strongname/api/common.h
index 26c545cff9..626d9bb720 100644
--- a/src/strongname/api/common.h
+++ b/src/strongname/api/common.h
@@ -146,6 +146,9 @@ typedef DPTR(class ReJitManager) PTR_ReJitManager;
typedef DPTR(struct ReJitInfo) PTR_ReJitInfo;
typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo;
typedef DPTR(class StringObject) PTR_StringObject;
+#ifdef FEATURE_UTF8STRING
+typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject;
+#endif // FEATURE_UTF8STRING
typedef DPTR(class TypeHandle) PTR_TypeHandle;
#ifdef STUB_DISPATCH
typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
diff --git a/src/tools/metainfo/metainfo.cpp b/src/tools/metainfo/metainfo.cpp
index ab2d73ad9d..afca3bf54a 100644
--- a/src/tools/metainfo/metainfo.cpp
+++ b/src/tools/metainfo/metainfo.cpp
@@ -26,7 +26,7 @@ void DisplayArchive(__in_z __in wchar_t* szFile, ULONG DumpFilter, __in_z __in_o
void PrintLogo()
{
- printf("Microsoft (R) .Net Frameworks Runtime Meta Data Dump Utility Version %s\n", VER_FILEVERSION_STR);
+ printf("Microsoft (R) .NET Frameworks Runtime Meta Data Dump Utility Version %s\n", VER_FILEVERSION_STR);
printf("%S", VER_LEGALCOPYRIGHT_LOGO_STR_L);
printf("\n");
}// PrintLogo
diff --git a/src/utilcode/registrywrapper.cpp b/src/utilcode/registrywrapper.cpp
index b3c4b42946..0d94a44dc1 100644
--- a/src/utilcode/registrywrapper.cpp
+++ b/src/utilcode/registrywrapper.cpp
@@ -6,7 +6,7 @@
//
//
-// Wrapper around Win32 Registry Functions allowing redirection of .Net
+// Wrapper around Win32 Registry Functions allowing redirection of .NET
// Framework root registry location
//
// Notes on Offline Ngen Implementation:
@@ -55,7 +55,7 @@
// has made incompatible changes here, such as moving the location or redefining
// values, we would break.
// - Accesses from C:\Windows\System32 and C:\Windows\Syswow64 and HKCU
-// HKCU does not contain any .Net Framework settings (Microsoft\.NETFramework
+// HKCU does not contain any .NET Framework settings (Microsoft\.NETFramework
// is empty).
// There are various files accessed from C:\Windows\System32 and these are a
// function of the OS loader. We load an executable and it automatically
diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp
index 5acb57914f..cabb19bc1e 100644
--- a/src/utilcode/util.cpp
+++ b/src/utilcode/util.cpp
@@ -206,6 +206,84 @@ typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,
EXTERN_C const IID _IID_IClassFactory =
{0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
+namespace
+{
+ HRESULT FakeCoCallDllGetClassObject(
+ REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void **ppv,
+ HMODULE *phmodDll)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppv != nullptr);
+
+ HRESULT hr = S_OK;
+
+ // Initialize [out] HMODULE (if it was requested)
+ if (phmodDll != nullptr)
+ *phmodDll = nullptr;
+
+ bool fIsDllPathPrefix = (wszDllPath != nullptr) && (wcslen(wszDllPath) > 0) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
+
+ // - An empty string will be treated as NULL.
+ // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
+ // if the InProcServer32 value is just a DLL name and not a full path.
+ StackSString ssDllName;
+ if ((wszDllPath == nullptr) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
+ {
+#ifndef FEATURE_PAL
+ IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
+
+ EX_TRY
+ {
+ if (fIsDllPathPrefix)
+ {
+ SString::Iterator i = ssDllName.Begin();
+ if (!ssDllName.Find(i, W('\\')))
+ { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
+ // prefix wszFilePath with wszDllPath.
+ ssDllName.Insert(i, wszDllPath);
+ }
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ wszDllPath = ssDllName.GetUnicode();
+#else // !FEATURE_PAL
+ return E_FAIL;
+#endif // !FEATURE_PAL
+ }
+ _ASSERTE(wszDllPath != nullptr);
+
+ // We've got the name of the DLL to load, so load it.
+ HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, nullptr, GetLoadWithAlteredSearchPathFlag());
+ if (hDll == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ // We've loaded the DLL, so find the DllGetClassObject function.
+ DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
+ if (dllGetClassObject == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ // Call the function to get a class object for the rclsid and riid passed in.
+ IfFailRet(dllGetClassObject(rclsid, riid, ppv));
+
+ hDll.SuppressRelease();
+
+ if (phmodDll != nullptr)
+ *phmodDll = hDll.GetValue();
+
+ return hr;
+ }
+}
+
// ----------------------------------------------------------------------------
// FakeCoCreateInstanceEx
//
@@ -223,8 +301,7 @@ EXTERN_C const IID _IID_IClassFactory =
// If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix
// if the InprocServer32 found in the registry is a simple filename (not a full path).
// This allows the caller to specify the directory in which the InprocServer32 should
-// be found. Also, if this path is provided and the InprocServer32 is MSCOREE.DLL, then
-// the Server value is used instead, if it exists.
+// be found.
// * riid - [in] IID of interface on object to return in ppv
// * ppv - [out] Pointer to implementation of requested interface
// * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object.
@@ -275,100 +352,6 @@ HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
return hr;
}
-HRESULT FakeCoCallDllGetClassObject(REFCLSID rclsid,
- LPCWSTR wszDllPath,
- REFIID riid,
- void ** ppv,
- HMODULE * phmodDll)
-{
- CONTRACTL
- {
- THROWS;
- }
- CONTRACTL_END;
-
- _ASSERTE(ppv != NULL);
-
- HRESULT hr = S_OK;
-
- if (phmodDll != NULL)
- { // Initialize [out] HMODULE (if it was requested)
- *phmodDll = NULL;
- }
-
- bool fIsDllPathPrefix = (wszDllPath != NULL) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
-
- // - An empty string will be treated as NULL.
- // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
- // if the InProcServer32 value is just a DLL name and not a full path.
- StackSString ssDllName;
- if ((wszDllPath == NULL) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
- {
-#ifndef FEATURE_PAL
- IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
-
- EX_TRY
- {
- if (fIsDllPathPrefix)
- {
- if (Clr::Util::Com::IsMscoreeInprocServer32(ssDllName))
- { // If the InprocServer32 is mscoree.dll, then we skip the shim and look for
- // the corresponding server DLL (if it exists) in the directory provided.
- hr = Clr::Util::Com::FindServerUsingCLSID(rclsid, ssDllName);
-
- if (FAILED(hr))
- { // We don't fail if there is no server object, because in this case we assume that
- // the clsid is implemented in the runtime itself (clr.dll) and we do not place
- // entries in the registry for this case.
- ssDllName.Set(MAIN_CLR_MODULE_NAME_W);
- }
- }
-
- SString::Iterator i = ssDllName.Begin();
- if (!ssDllName.Find(i, W('\\')))
- { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
- // prefix wszFilePath with wszDllPath.
- ssDllName.Insert(i, wszDllPath);
- }
- }
- }
- EX_CATCH_HRESULT(hr);
- IfFailRet(hr);
-
- wszDllPath = ssDllName.GetUnicode();
-#else // !FEATURE_PAL
- return E_FAIL;
-#endif // !FEATURE_PAL
- }
- _ASSERTE(wszDllPath != NULL);
-
- // We've got the name of the DLL to load, so load it.
- HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, NULL, GetLoadWithAlteredSearchPathFlag());
- if (hDll == NULL)
- {
- return HRESULT_FROM_GetLastError();
- }
-
- // We've loaded the DLL, so find the DllGetClassObject function.
- DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
- if (dllGetClassObject == NULL)
- {
- return HRESULT_FROM_GetLastError();
- }
-
- // Call the function to get a class object for the rclsid and riid passed in.
- IfFailRet(dllGetClassObject(rclsid, riid, ppv));
-
- hDll.SuppressRelease();
-
- if (phmodDll != NULL)
- {
- *phmodDll = hDll.GetValue();
- }
-
- return hr;
-}
-
#if USE_UPPER_ADDRESS
static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
static BYTE * s_CodeMaxAddr;
@@ -3128,6 +3111,101 @@ namespace Util
}
#ifndef FEATURE_PAL
+ // Struct used to scope suspension of client impersonation for the current thread.
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthz/client-impersonation
+ class SuspendImpersonation
+ {
+ public:
+ SuspendImpersonation()
+ : _token(nullptr)
+ {
+ // The approach used here matches what is used elsewhere in CLR (RevertIfImpersonated).
+ // In general, OpenThreadToken fails with ERROR_NO_TOKEN if impersonation is not active,
+ // fails with ERROR_CANT_OPEN_ANONYMOUS if anonymous impersonation is active, and otherwise
+ // succeeds and returns the active impersonation token.
+ BOOL res = ::OpenThreadToken(::GetCurrentThread(), TOKEN_IMPERSONATE, /* OpenAsSelf */ TRUE, &_token);
+ if (res != FALSE)
+ {
+ ::RevertToSelf();
+ }
+ else
+ {
+ _token = nullptr;
+ }
+ }
+
+ ~SuspendImpersonation()
+ {
+ if (_token != nullptr)
+ ::SetThreadToken(nullptr, _token);
+ }
+
+ private:
+ HandleHolder _token;
+ };
+
+ struct ProcessIntegrityResult
+ {
+ BOOL Success;
+ DWORD Integrity;
+ HRESULT LastError;
+
+ HRESULT RecordAndReturnError(HRESULT hr)
+ {
+ LastError = hr;
+ return hr;
+ }
+ };
+
+ // The system calls in this code can fail if run with reduced privileges.
+ // It is the caller's responsibility to choose an appropriate default in the event
+ // that this function fails to retrieve the current process integrity.
+ HRESULT GetCurrentProcessIntegrity(DWORD *integrity)
+ {
+ static ProcessIntegrityResult s_Result = { FALSE, 0, S_FALSE };
+
+ if (FALSE != InterlockedCompareExchangeT(&s_Result.Success, FALSE, FALSE))
+ {
+ *integrity = s_Result.Integrity;
+ return S_OK;
+ }
+
+ // Temporarily suspend impersonation (if possible) while computing the integrity level.
+ // If impersonation is active, the OpenProcessToken call below will check the impersonation
+ // token against the process token ACL, and will generally fail with ERROR_ACCESS_DENIED if
+ // the impersonation token is less privileged than this process's primary token.
+ Clr::Util::SuspendImpersonation si;
+
+ HandleHolder hToken;
+ if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
+ return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
+
+ DWORD dwSize = 0;
+ DWORD err = ERROR_SUCCESS;
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, nullptr, 0, &dwSize))
+ err = GetLastError();
+
+ // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
+ // dwSize has the correct buffer size in it.
+ if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
+ return s_Result.RecordAndReturnError((err == ERROR_SUCCESS) ? E_FAIL : HRESULT_FROM_WIN32(err));
+
+ NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
+ if (pLabel == NULL)
+ return s_Result.RecordAndReturnError(E_OUTOFMEMORY);
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
+ return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
+
+ TOKEN_MANDATORY_LABEL *ptml = (TOKEN_MANDATORY_LABEL *)(void*)pLabel;
+ PSID psidIntegrityLevelLabel = ptml->Label.Sid;
+
+ s_Result.Integrity = *GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
+ *integrity = s_Result.Integrity;
+ InterlockedExchangeT(&s_Result.Success, TRUE);
+ return S_OK;
+ }
+
namespace Reg
{
HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
@@ -3223,8 +3301,6 @@ namespace Com
{
STANDARD_VM_CONTRACT;
- HRESULT hr = S_OK;
-
WCHAR wszClsid[39];
if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
return E_UNEXPECTED;
@@ -3235,49 +3311,38 @@ namespace Com
ssKeyName.Append(SL(W("\\")));
ssKeyName.Append(wszSubKeyName);
- return Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), NULL, ssValue);
- }
-
- __success(return == S_OK)
- static
- HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, __deref_out __deref_out_z LPWSTR* pwszValue)
- {
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- } CONTRACTL_END;
+ // Query HKCR first to retain backwards compat with previous implementation where HKCR was only queried.
+ // This is being done due to registry caching. This value will be used if the process integrity is medium or less.
+ HRESULT hkcrResult = Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), nullptr, ssValue);
- HRESULT hr = S_OK;
- EX_TRY
+ // HKCR is a virtualized registry hive that weaves together HKCU\Software\Classes and HKLM\Software\Classes
+ // Processes with high integrity or greater should only read from HKLM to avoid being hijacked by medium
+ // integrity processes writing to HKCU.
+ DWORD integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
+ HRESULT hr = Clr::Util::GetCurrentProcessIntegrity(&integrity);
+ if (hr != S_OK)
{
- StackSString ssValue;
- if (SUCCEEDED(hr = FindSubKeyDefaultValueForCLSID(rclsid, wszSubKeyName, ssValue)))
- {
- *pwszValue = new WCHAR[ssValue.GetCount() + 1];
- wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
- }
+ // In the event that we are unable to get the current process integrity,
+ // we assume that this process is running in an elevated state.
+ // GetCurrentProcessIntegrity may fail if the process has insufficient rights to get the integrity level
+ integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
}
- EX_CATCH_HRESULT(hr);
- return hr;
- }
- }
- HRESULT FindServerUsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszServerName)
- {
- WRAPPER_NO_CONTRACT;
- return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), pwszServerName);
- }
+ if (integrity > SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ Clr::Util::SuspendImpersonation si;
- HRESULT FindServerUsingCLSID(REFCLSID rclsid, SString & ssServerName)
- {
- WRAPPER_NO_CONTRACT;
- return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), ssServerName);
- }
+ // Clear the previous HKCR queried value
+ ssValue.Clear();
- HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszInprocServer32Name)
- {
- WRAPPER_NO_CONTRACT;
- return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), pwszInprocServer32Name);
+ // Force to use HKLM
+ StackSString ssHklmKeyName(SL(W("SOFTWARE\\Classes\\")));
+ ssHklmKeyName.Append(ssKeyName);
+ return Clr::Util::Reg::ReadStringValue(HKEY_LOCAL_MACHINE, ssHklmKeyName.GetUnicode(), nullptr, ssValue);
+ }
+
+ return hkcrResult;
+ }
}
HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
@@ -3285,24 +3350,6 @@ namespace Com
WRAPPER_NO_CONTRACT;
return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
}
-
- BOOL IsMscoreeInprocServer32(const SString & ssInprocServer32Name)
- {
- WRAPPER_NO_CONTRACT;
-
- return (ssInprocServer32Name.EqualsCaseInsensitive(SL(MSCOREE_SHIM_W)) ||
- ssInprocServer32Name.EndsWithCaseInsensitive(SL(W("\\") MSCOREE_SHIM_W)));
- }
-
- BOOL CLSIDHasMscoreeAsInprocServer32(REFCLSID rclsid)
- {
- WRAPPER_NO_CONTRACT;
-
- StackSString ssInprocServer32;
- FindInprocServer32UsingCLSID(rclsid, ssInprocServer32);
- return IsMscoreeInprocServer32(ssInprocServer32);
- }
-
} // namespace Com
#endif // FEATURE_PAL
@@ -3385,7 +3432,6 @@ namespace Win32
// Overly defensive? Perhaps.
if (!(dwLengthWritten < dwLengthRequired))
ThrowHR(E_UNEXPECTED);
-
}
} // namespace Win32
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 397f4e2ea5..b7ae17a156 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -306,6 +306,7 @@ set(VM_SOURCES_WKS
comwaithandle.cpp
customattribute.cpp
custommarshalerinfo.cpp
+ diagnosticserver.cpp
dllimportcallback.cpp
eeconfig.cpp
eecontract.cpp
@@ -319,12 +320,15 @@ set(VM_SOURCES_WKS
eventpipeeventsource.cpp
eventpipeblock.cpp
eventpipefile.cpp
+ eventpipeinternal.cpp
eventpipejsonfile.cpp
eventpipemetadatagenerator.cpp
+ eventpipeprotocolhelper.cpp
eventpipeprovider.cpp
eventpipebuffer.cpp
eventpipebuffermanager.cpp
eventpipesession.cpp
+ eventpipesessionprovider.cpp
eventstore.cpp
fastserializer.cpp
fcall.cpp
@@ -421,6 +425,8 @@ set(VM_HEADERS_WKS
comwaithandle.h
customattribute.h
custommarshalerinfo.h
+ diagnosticserver.h
+ diagnosticsprotocol.h
dllimportcallback.h
eeconfig.h
eecontract.h
@@ -439,10 +445,13 @@ set(VM_HEADERS_WKS
eventpipeeventinstance.h
eventpipeeventsource.h
eventpipefile.h
+ eventpipeinternal.h
eventpipejsonfile.h
eventpipemetadatagenerator.h
+ eventpipeprotocolhelper.h
eventpipeprovider.h
eventpipesession.h
+ eventpipesessionprovider.h
eventstore.hpp
fastserializer.h
fcall.h
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index 6aff6ab60a..9362dd9c7b 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -2485,6 +2485,11 @@ void SystemDomain::LoadBaseSystemClasses()
// Load String
g_pStringClass = MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING);
+#ifdef FEATURE_UTF8STRING
+ // Load Utf8String
+ g_pUtf8StringClass = MscorlibBinder::GetClass(CLASS__UTF8_STRING);
+#endif // FEATURE_UTF8STRING
+
// Used by Buffer::BlockCopy
g_pByteArrayMT = ClassLoader::LoadArrayTypeThrowing(
TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U1))).AsArray()->GetMethodTable();
@@ -2839,6 +2844,7 @@ MethodTable *AppDomain::LoadCOMClass(GUID clsid,
BOOL* pfAssemblyInReg/*=NULL*/)
{
// @CORESYSTODO: what to do here?
+ // If implemented, this should handle checking that the type actually has the requested CLSID
return NULL;
}
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index c0fad3c595..1fec2c262d 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -191,6 +191,35 @@ BOOL Module::SetTransientFlagInterlocked(DWORD dwFlag)
}
#if PROFILING_SUPPORTED
+void Module::UpdateNewlyAddedTypes()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ DWORD countTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
+ DWORD countExportedTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+
+ // typeDefs rids 0 and 1 aren't included in the count, thus X typeDefs before means rid X+1 was valid and our incremental addition should start at X+2
+ for (DWORD typeDefRid = m_dwTypeCount + 2; typeDefRid < countTypesAfterProfilerUpdate + 2; typeDefRid++)
+ {
+ GetAssembly()->AddType(this, TokenFromRid(typeDefRid, mdtTypeDef));
+ }
+
+ // exportedType rid 0 isn't included in the count, thus X exportedTypes before means rid X was valid and our incremental addition should start at X+1
+ for (DWORD exportedTypeDef = m_dwExportedTypeCount + 1; exportedTypeDef < countExportedTypesAfterProfilerUpdate + 1; exportedTypeDef++)
+ {
+ GetAssembly()->AddExportedType(TokenFromRid(exportedTypeDef, mdtExportedType));
+ }
+
+ m_dwTypeCount = countTypesAfterProfilerUpdate;
+ m_dwExportedTypeCount = countExportedTypesAfterProfilerUpdate;
+}
+
void Module::NotifyProfilerLoadFinished(HRESULT hr)
{
CONTRACTL
@@ -208,12 +237,10 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr)
if (SetTransientFlagInterlocked(IS_PROFILER_NOTIFIED))
{
// Record how many types are already present
- DWORD countTypesOrig = 0;
- DWORD countExportedTypesOrig = 0;
if (!IsResource())
{
- countTypesOrig = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
- countExportedTypesOrig = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+ m_dwTypeCount = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
+ m_dwExportedTypeCount = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
}
// Notify the profiler, this may cause metadata to be updated
@@ -236,18 +263,7 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr)
// assembly
if (!IsResource())
{
- DWORD countTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
- DWORD countExportedTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
- // typeDefs rids 0 and 1 aren't included in the count, thus X typeDefs before means rid X+1 was valid and our incremental addition should start at X+2
- for (DWORD typeDefRid = countTypesOrig + 2; typeDefRid < countTypesAfterProfilerUpdate + 2; typeDefRid++)
- {
- GetAssembly()->AddType(this, TokenFromRid(typeDefRid, mdtTypeDef));
- }
- // exportedType rid 0 isn't included in the count, thus X exportedTypes before means rid X was valid and our incremental addition should start at X+1
- for (DWORD exportedTypeDef = countExportedTypesOrig + 1; exportedTypeDef < countExportedTypesAfterProfilerUpdate + 1; exportedTypeDef++)
- {
- GetAssembly()->AddExportedType(TokenFromRid(exportedTypeDef, mdtExportedType));
- }
+ UpdateNewlyAddedTypes();
}
{
@@ -585,6 +601,11 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
m_ModuleID = NULL;
m_ModuleIndex.m_dwIndex = (SIZE_T)-1;
+ // These will be initialized in NotifyProfilerLoadFinished, set them to
+ // a safe initial value now.
+ m_dwTypeCount = 0;
+ m_dwExportedTypeCount = 0;
+
// Prepare statics that are known at module load time
AllocateStatics(pamTracker);
@@ -1187,7 +1208,7 @@ void Module::ApplyMetaData()
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -1197,6 +1218,13 @@ void Module::ApplyMetaData()
HRESULT hr = S_OK;
ULONG ulCount;
+#if PROFILING_SUPPORTED
+ if (!IsResource())
+ {
+ UpdateNewlyAddedTypes();
+ }
+#endif // PROFILING_SUPPORTED
+
// Ensure for TypeRef
ulCount = GetMDImport()->GetCountWithTokenKind(mdtTypeRef) + 1;
EnsureTypeRefCanBeStored(TokenFromRid(ulCount, mdtTypeRef));
@@ -1204,6 +1232,10 @@ void Module::ApplyMetaData()
// Ensure for AssemblyRef
ulCount = GetMDImport()->GetCountWithTokenKind(mdtAssemblyRef) + 1;
EnsureAssemblyRefCanBeStored(TokenFromRid(ulCount, mdtAssemblyRef));
+
+ // Ensure for MethodDef
+ ulCount = GetMDImport()->GetCountWithTokenKind(mdtMethodDef) + 1;
+ EnsureMethodDefCanBeStored(TokenFromRid(ulCount, mdtMethodDef));
}
//
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index 6bf5fbdd3b..01d5ba880a 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -187,7 +187,7 @@ typedef DPTR(struct LookupMapBase) PTR_LookupMapBase;
// importantly we cannot mutate compressed entries (for obvious reasons). Many of the lookup maps are only
// partially populated at ngen time or otherwise might be updated at runtime and thus are not candidates.
//
-// In the threshhold timeframe (predicted to be .Net 4.5.3 at the time of writing), we added profiler support
+// In the threshhold timeframe (predicted to be .NET Framework 4.5.3 at the time of writing), we added profiler support
// for adding new types to NGEN images. Historically we could always do this for jitted images, but one of the
// blockers for NGEN were the compressed RID maps. We worked around that by supporting multi-node maps in which
// the first node is compressed, but all future nodes are uncompressed. The NGENed portion will all land in the
@@ -1627,6 +1627,11 @@ private:
BOOL m_nativeImageProfiling;
CORCOMPILE_METHOD_PROFILE_LIST *m_methodProfileList;
+#if PROFILING_SUPPORTED_DATA
+ DWORD m_dwTypeCount;
+ DWORD m_dwExportedTypeCount;
+#endif // PROFILING_SUPPORTED_DATA
+
#if defined(FEATURE_COMINTEROP)
public:
@@ -2526,6 +2531,8 @@ public:
DEBUGGER_INFO_SHIFT_PRIV);
}
+ void UpdateNewlyAddedTypes();
+
#ifdef PROFILING_SUPPORTED
BOOL IsProfilerNotified() {LIMITED_METHOD_CONTRACT; return (m_dwTransientFlags & IS_PROFILER_NOTIFIED) != 0; }
void NotifyProfilerLoadFinished(HRESULT hr);
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index e5267cf131..1b85649d67 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -215,6 +215,7 @@
#include "perfmap.h"
#endif
+#include "diagnosticserver.h"
#include "eventpipe.h"
#ifndef FEATURE_PAL
@@ -667,6 +668,7 @@ void EEStartupHelper(COINITIEE fFlags)
#ifdef FEATURE_PERFTRACING
// Initialize the event pipe.
+ DiagnosticServer::Initialize();
EventPipe::Initialize();
#endif // FEATURE_PERFTRACING
@@ -1462,6 +1464,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading)
#ifdef FEATURE_PERFTRACING
// Shutdown the event pipe.
EventPipe::Shutdown();
+ DiagnosticServer::Shutdown();
#endif // FEATURE_PERFTRACING
#if defined(FEATURE_COMINTEROP)
diff --git a/src/vm/classnames.h b/src/vm/classnames.h
index cb71df362a..f45311f0de 100644
--- a/src/vm/classnames.h
+++ b/src/vm/classnames.h
@@ -139,6 +139,10 @@
#define g_ThreadClassName "System.Threading.Thread"
#define g_TypeClassName "System.Type"
+#ifdef FEATURE_UTF8STRING
+#define g_Utf8StringName "Utf8String"
+#endif // FEATURE_UTF8STRING
+
#define g_VariantClassName "System.Variant"
#define g_GuidClassName "System.Guid"
diff --git a/src/vm/clsload.cpp b/src/vm/clsload.cpp
index 4ffbb3fced..323e29ba60 100644
--- a/src/vm/clsload.cpp
+++ b/src/vm/clsload.cpp
@@ -840,7 +840,8 @@ void ClassLoader::GetClassValue(NameHandleTable nhTable,
continue;
#ifdef FEATURE_READYTORUN
- if (nhTable == nhCaseSensitive && pCurrentClsModule->IsReadyToRun() && pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes())
+ if (nhTable == nhCaseSensitive && pCurrentClsModule->IsReadyToRun() && pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes() &&
+ pCurrentClsModule->GetAvailableClassHash() == NULL)
{
// For R2R modules, we only search the hashtable of token types stored in the module's image, and don't fallback
// to searching m_pAvailableClasses or m_pAvailableClassesCaseIns (in fact, we don't even allocate them for R2R modules).
@@ -1618,7 +1619,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
EEClassHashEntry_t * pBucket = foundEntry.GetClassHashBasedEntryValue();
- if (pBucket == NULL && needsToBuildHashtable)
+ if (pBucket == NULL)
{
AvailableClasses_LockHolder lh(this);
@@ -1628,7 +1629,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
pBucket = foundEntry.GetClassHashBasedEntryValue();
#ifndef DACCESS_COMPILE
- if ((pBucket == NULL) && (m_cUnhashedModules > 0))
+ if (needsToBuildHashtable && (pBucket == NULL) && (m_cUnhashedModules > 0))
{
_ASSERT(needsToBuildHashtable);
@@ -1650,6 +1651,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
#endif
}
+ // Same check as above, but this time we've checked with the lock so the table will be populated
if (pBucket == NULL)
{
#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
@@ -4184,6 +4186,15 @@ VOID ClassLoader::AddAvailableClassDontHaveLock(Module *pModule,
#endif
CrstHolder ch(&m_AvailableClassLock);
+
+ // R2R pre-computes an export table and tries to avoid populating a class hash at runtime. However the profiler can
+ // still add new types on the fly by calling here. If that occurs we fallback to the slower path of creating the
+ // in memory hashtable as usual.
+ if (!pModule->IsResource() && pModule->GetAvailableClassHash() == NULL)
+ {
+ LazyPopulateCaseSensitiveHashTables();
+ }
+
AddAvailableClassHaveLock(
pModule,
classdef,
@@ -4380,6 +4391,15 @@ VOID ClassLoader::AddExportedTypeDontHaveLock(Module *pManifestModule,
CONTRACTL_END
CrstHolder ch(&m_AvailableClassLock);
+
+ // R2R pre-computes an export table and tries to avoid populating a class hash at runtime. However the profiler can
+ // still add new types on the fly by calling here. If that occurs we fallback to the slower path of creating the
+ // in memory hashtable as usual.
+ if (!pManifestModule->IsResource() && pManifestModule->GetAvailableClassHash() == NULL)
+ {
+ LazyPopulateCaseSensitiveHashTables();
+ }
+
AddExportedTypeHaveLock(
pManifestModule,
cl,
diff --git a/src/vm/codeversion.cpp b/src/vm/codeversion.cpp
index e5e8a75e80..9fceca5a5b 100644
--- a/src/vm/codeversion.cpp
+++ b/src/vm/codeversion.cpp
@@ -2589,7 +2589,7 @@ void CodeVersionManager::OnAppDomainExit(AppDomain * pAppDomain)
LIMITED_METHOD_CONTRACT;
// This would clean up all the allocations we have done and synchronize with any threads that might
// still be using the data
- _ASSERTE(!".Net Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented");
+ _ASSERTE(!".NET Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented");
}
#endif
diff --git a/src/vm/comdelegate.cpp b/src/vm/comdelegate.cpp
index 9a131fa126..42f457ff3a 100644
--- a/src/vm/comdelegate.cpp
+++ b/src/vm/comdelegate.cpp
@@ -1626,9 +1626,35 @@ FCIMPL3(PCODE, COMDelegate::AdjustTarget, Object* refThisUNSAFE, Object* targetU
// close delegates
MethodTable* pMTTarg = target->GetMethodTable();
MethodTable* pMTMeth = pMeth->GetMethodTable();
+
+ BOOL isComObject = false;
+
+#ifdef FEATURE_COMINTEROP
+ isComObject = pMTTarg->IsComObjectType();
+#endif // FEATURE_COMINTEROP
MethodDesc *pCorrectedMethod = pMeth;
+ if (pMTMeth != pMTTarg)
+ {
+ //They cast to an interface before creating the delegate, so we now need
+ //to figure out where this actually lives before we continue.
+ //<TODO>@perf: Grovelling with a signature is really slow. Speed this up.</TODO>
+ if (pCorrectedMethod->IsInterface())
+ {
+ // No need to resolve the interface based method desc to a class based
+ // one for COM objects because we invoke directly thru the interface MT.
+ if (!isComObject)
+ {
+ // <TODO>it looks like we need to pass an ownerType in here.
+ // Why can we take a delegate to an interface method anyway? </TODO>
+ //
+ pCorrectedMethod = pMTTarg->FindDispatchSlotForInterfaceMD(pCorrectedMethod, TRUE /* throwOnConflict */).GetMethodDesc();
+ _ASSERTE(pCorrectedMethod != NULL);
+ }
+ }
+ }
+
// Use the Unboxing stub for value class methods, since the value
// class is constructed using the boxed instance.
if (pCorrectedMethod->GetMethodTable()->IsValueType() && !pCorrectedMethod->IsUnboxingStub())
@@ -1772,6 +1798,53 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
{
if (pMTTarg)
{
+ // We can skip the demand if SuppressUnmanagedCodePermission is present on the class,
+ // or in the case where we are setting up a delegate for a COM event sink
+ // we can skip the check if the source interface is defined in fully trusted code
+ // we can skip the check if the source interface is a disp-only interface
+ BOOL isComObject = false;
+#ifdef FEATURE_COMINTEROP
+ isComObject = pMTTarg->IsComObjectType();
+#endif // FEATURE_COMINTEROP
+
+ if (pMTMeth != pMTTarg)
+ {
+ // They cast to an interface before creating the delegate, so we now need
+ // to figure out where this actually lives before we continue.
+ // <TODO>@perf: We whould never be using this path to invoke on an interface -
+ // that should always be resolved when we are creating the delegate </TODO>
+ if (pMeth->IsInterface())
+ {
+ // No need to resolve the interface based method desc to a class based
+ // one for COM objects because we invoke directly thru the interface MT.
+ if (!isComObject)
+ {
+ // <TODO>it looks like we need to pass an ownerType in here.
+ // Why can we take a delegate to an interface method anyway? </TODO>
+ //
+ MethodDesc * pDispatchSlotMD = pMTTarg->FindDispatchSlotForInterfaceMD(pMeth, TRUE /* throwOnConflict */).GetMethodDesc();
+ if (pDispatchSlotMD == NULL)
+ {
+ COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth"));
+ }
+
+ if (pMeth->HasMethodInstantiation())
+ {
+ pMeth = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pDispatchSlotMD,
+ pMTTarg,
+ (!pDispatchSlotMD->IsStatic() && pMTTarg->IsValueType()),
+ pMeth->GetMethodInstantiation(),
+ FALSE /* allowInstParam */);
+ }
+ else
+ {
+ pMeth = pDispatchSlotMD;
+ }
+ }
+ }
+ }
+
g_IBCLogger.LogMethodTableAccess(pMTTarg);
// Use the Unboxing stub for value class methods, since the value
@@ -3157,9 +3230,10 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
#endif
// under the conditions below the delegate ctor needs to perform some heavy operation
- // to get the unboxing stub
+ // to either resolve the interface call to the real target or to get the unboxing stub (or both)
BOOL needsRuntimeInfo = !pTargetMethod->IsStatic() &&
- pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub();
+ (pTargetMethod->IsInterface() ||
+ (pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub()));
if (needsRuntimeInfo)
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_RT_CLOSED);
diff --git a/src/vm/common.h b/src/vm/common.h
index 9050ae7c63..61ba2a7514 100644
--- a/src/vm/common.h
+++ b/src/vm/common.h
@@ -75,7 +75,6 @@
#include <stdlib.h>
#include <wchar.h>
#include <objbase.h>
-#include <stddef.h>
#include <float.h>
#include <math.h>
#include <time.h>
@@ -168,6 +167,9 @@ typedef DPTR(class ReJitManager) PTR_ReJitManager;
typedef DPTR(struct ReJitInfo) PTR_ReJitInfo;
typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo;
typedef DPTR(class StringObject) PTR_StringObject;
+#ifdef FEATURE_UTF8STRING
+typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject;
+#endif // FEATURE_UTF8STRING
typedef DPTR(class TypeHandle) PTR_TypeHandle;
typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager;
diff --git a/src/vm/compile.cpp b/src/vm/compile.cpp
index bebe403b16..47147976bc 100644
--- a/src/vm/compile.cpp
+++ b/src/vm/compile.cpp
@@ -3050,7 +3050,7 @@ HRESULT NGenModulePdbWriter::WritePDBData()
// Currently DiaSymReader does not work properly generating NGEN PDBS unless
// the DLL whose PDB is being generated ends in .ni.*. Unfortunately, readyToRun
// images do not follow this convention and end up producing bad PDBS. To fix
- // this (without changing diasymreader.dll which ships indepdendently of .Net Core)
+ // this (without changing diasymreader.dll which ships indepdendently of .NET Core)
// we copy the file to somethign with this convention before generating the PDB
// and delete it when we are done.
SString dllPath = pLoadedLayout->GetPath();
diff --git a/src/vm/crossgencompile.cpp b/src/vm/crossgencompile.cpp
index d357bd7622..de07c81ef2 100644
--- a/src/vm/crossgencompile.cpp
+++ b/src/vm/crossgencompile.cpp
@@ -363,10 +363,14 @@ extern "C" UINT_PTR STDCALL GetCurrentIP()
return 0;
}
-void EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString)
+// This method must return a value to avoid getting non-actionable dumps on x86.
+// If this method were a DECLSPEC_NORETURN then dumps would not provide the necessary
+// context at the point of the failure
+int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString)
{
fprintf(stderr, "Fatal error: %08x\n", exitCode);
ExitProcess(exitCode);
+ return -1;
}
//---------------------------------------------------------------------------------------
diff --git a/src/vm/diagnosticserver.cpp b/src/vm/diagnosticserver.cpp
new file mode 100644
index 0000000000..c0e11d100f
--- /dev/null
+++ b/src/vm/diagnosticserver.cpp
@@ -0,0 +1,171 @@
+// 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.
+
+#include "common.h"
+#include "diagnosticserver.h"
+#include "diagnosticsipc.h"
+#include "eventpipeprotocolhelper.h"
+
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter)
+{
+ CONTRACTL
+ {
+ // TODO: Maybe this should not throw.
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(lpThreadParameter != nullptr);
+ }
+ CONTRACTL_END;
+
+ auto pIpc = reinterpret_cast<IpcStream::DiagnosticsIpc *>(lpThreadParameter);
+ if (pIpc == nullptr)
+ {
+ STRESS_LOG0(LF_STARTUP, LL_ERROR,"Diagnostics IPC listener was undefined\n");
+ return 1;
+ }
+
+#ifdef _DEBUG
+ ErrorCallback LoggingCallback = [](const char *szMessage, uint32_t code) {
+ LOG((LF_REMOTING, LL_WARNING, "warning (%d): %s.\n", code, szMessage));
+ };
+#else
+ ErrorCallback LoggingCallback = nullptr;
+#endif
+
+ while (true)
+ {
+ // FIXME: Ideally this would be something like a std::shared_ptr
+ IpcStream *pStream = pIpc->Accept(LoggingCallback);
+ if (pStream == nullptr)
+ continue;
+
+ // TODO: Read operation should happen in a loop.
+ uint32_t nNumberOfBytesRead = 0;
+ MessageHeader header;
+ bool fSuccess = pStream->Read(&header, sizeof(header), nNumberOfBytesRead);
+ if (!fSuccess || nNumberOfBytesRead != sizeof(header))
+ {
+ delete pStream;
+ continue;
+ }
+
+ // TODO: Dispatch thread worker.
+ switch (header.RequestType)
+ {
+ case DiagnosticMessageType::EnableEventPipe:
+ EventPipeProtocolHelper::EnableFileTracingEventHandler(pStream);
+ break;
+
+ case DiagnosticMessageType::DisableEventPipe:
+ EventPipeProtocolHelper::DisableTracingEventHandler(pStream);
+ break;
+
+ default:
+ LOG((LF_REMOTING, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+bool DiagnosticServer::Initialize()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ bool fSuccess = false;
+
+ EX_TRY
+ {
+ auto ErrorCallback = [](const char *szMessage, uint32_t code) {
+ STRESS_LOG2(
+ LF_STARTUP, // facility
+ LL_ERROR, // level
+ "Failed to create diagnostic IPC: error (%d): %s.\n", // msg
+ code, // data1
+ szMessage); // data2
+ };
+ IpcStream::DiagnosticsIpc *pIpc = IpcStream::DiagnosticsIpc::Create(
+ "dotnetcore-diagnostic", ErrorCallback);
+
+ if (pIpc != nullptr)
+ {
+ DWORD dwThreadId = 0;
+ HANDLE hThread = ::CreateThread( // TODO: Is it correct to have this "lower" level call here?
+ nullptr, // no security attribute
+ 0, // default stack size
+ DiagnosticsServerThread, // thread proc
+ (LPVOID)pIpc, // thread parameter
+ 0, // not suspended
+ &dwThreadId); // returns thread ID
+
+ if (hThread == nullptr)
+ {
+ // Failed to create IPC thread.
+ STRESS_LOG1(
+ LF_STARTUP, // facility
+ LL_ERROR, // level
+ "Failed to create diagnostic server thread (%d).\n", // msg
+ ::GetLastError()); // data1
+ }
+ else
+ {
+ // FIXME: Maybe hold on to the thread to abort/cleanup at exit?
+ ::CloseHandle(hThread);
+
+ // TODO: Add error handling?
+ fSuccess = true;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ // TODO: Should we log anything here?
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fSuccess;
+}
+
+bool DiagnosticServer::Shutdown()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ bool fSuccess = false;
+
+ EX_TRY
+ {
+ // FIXME: Stop IPC server thread?
+ fSuccess = true;
+ }
+ EX_CATCH
+ {
+ fSuccess = false;
+ // TODO: Should we log anything here?
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fSuccess;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/diagnosticserver.h b/src/vm/diagnosticserver.h
new file mode 100644
index 0000000000..95399a2ef8
--- /dev/null
+++ b/src/vm/diagnosticserver.h
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __DIAGNOSTIC_SERVER_H__
+#define __DIAGNOSTIC_SERVER_H__
+
+#include <stdint.h>
+
+#ifdef FEATURE_PERFTRACING // This macro should change to something more generic than performance.
+
+//! TODO: Temp class.
+enum class DiagnosticMessageType : uint32_t
+{
+ ///////////////////////////////////////////////////////////////////////////
+ // Debug = 0
+
+ ///////////////////////////////////////////////////////////////////////////
+ // EventPipe
+ EnableEventPipe = 1024,
+ DisableEventPipe,
+
+ // TODO: Define what else is available on the out-of-proc interface?
+ // GetSessionInfo,
+ // CreateProvider,
+ // DefineEvent,
+ // GetProvider,
+ // DeleteProvider,
+ // EventActivityIdControl,
+ // WriteEvent,
+ // WriteEventData,
+ // GetNextEvent,
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Profiler = 2048
+};
+
+//! TODO: Temp class.
+struct MessageHeader
+{
+ DiagnosticMessageType RequestType;
+ uint32_t Pid;
+};
+
+//! Defines an implementation of a IPC handler that dispatches messages to the runtime.
+class DiagnosticServer final
+{
+public:
+ //! Initialize the event pipe (Creates the EventPipe IPC server).
+ static bool Initialize();
+
+ //! Shutdown the event pipe.
+ static bool Shutdown();
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __DIAGNOSTIC_SERVER_H__
diff --git a/src/vm/diagnosticsprotocol.h b/src/vm/diagnosticsprotocol.h
new file mode 100644
index 0000000000..765977f02a
--- /dev/null
+++ b/src/vm/diagnosticsprotocol.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef __DIAGNOSTICS_PROTOCOL_H__
+#define __DIAGNOSTICS_PROTOCOL_H__
+
+#ifdef FEATURE_PERFTRACING
+
+template <typename T>
+bool TryParse(uint8_t *&bufferCursor, uint32_t &bufferLen, T &result)
+{
+ static_assert(
+ std::is_integral<T>::value || std::is_same<T, float>::value || std::is_same<T, double>::value,
+ "Can only be instantiated with integral and floating point types.");
+
+ if (bufferLen < sizeof(T))
+ return false;
+ result = *(reinterpret_cast<T *>(bufferCursor));
+ bufferCursor += sizeof(T);
+ bufferLen -= sizeof(T);
+ return true;
+}
+
+template <typename T>
+bool TryParseString(uint8_t *&bufferCursor, uint32_t &bufferLen, const T *&result)
+{
+ static_assert(
+ std::is_same<T, char>::value || std::is_same<T, wchar_t>::value,
+ "Can only be instantiated with char and wchar_t types.");
+
+ uint32_t stringLen = 0;
+ if (!TryParse(bufferCursor, bufferLen, stringLen))
+ return false;
+ if (stringLen == 0)
+ {
+ result = nullptr;
+ return true;
+ }
+ if (stringLen > (bufferLen / sizeof(T)))
+ return false;
+ if ((reinterpret_cast<const T *>(bufferCursor))[stringLen - 1] != 0)
+ return false;
+ result = reinterpret_cast<const T *>(bufferCursor);
+
+ const uint32_t TotalStringLength = stringLen * sizeof(T);
+ bufferCursor += TotalStringLength;
+ bufferLen -= TotalStringLength;
+ return true;
+}
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __DIAGNOSTICS_PROTOCOL_H__
diff --git a/src/vm/ecall.cpp b/src/vm/ecall.cpp
index b8e0d64e8f..dfeff95d6f 100644
--- a/src/vm/ecall.cpp
+++ b/src/vm/ecall.cpp
@@ -29,6 +29,36 @@ extern const int c_nECClasses;
#endif // CROSSGEN_COMPILE
+/**********
+
+The constructors of string-like types (String, Utf8String) are special since the JIT will
+replace newobj instructions with calls to the corresponding 'Ctor' method. Depending on the
+CLR in use, the ctor methods may be instance methods (with a null 'this' parameter) or
+static methods. See the managed definitions of String.Ctor and Utf8String.Ctor for more
+information.
+
+To add a new ctor overload, in addition to defining the constructor and Ctor methods on
+the managed side, make changes to the following files. (These instructions are for
+Utf8String, but String is similar.)
+
+- src/vm/ecall.cpp (this file), update the definition of "NumberOfUtf8StringConstructors"
+ and add the appropriate static asserts immediately above the definition.
+
+- src/vm/ecall.h, search for "Utf8StringCtor" and add the DYNAMICALLY_ASSIGNED_FCALL_IMPL
+ definitions corresponding to the new overloads.
+
+- src/vm/ecalllist.h, search for "FCFuncStart(gUtf8StringFuncs)" and add the overloads
+ within that block.
+
+- src/vm/metasig.h, add the new Utf8String-returning metasig declarations; and, if necessary,
+ add any void-returning metasig declarations if they haven't already been defined elsewhere.
+ search "String_RetUtf8Str" for an example of how to do this.
+
+- src/vm/mscorlib.h, search "DEFINE_CLASS(UTF8_STRING" and add the new DEFINE_METHOD
+ declarations for the Utf8String-returning Ctor methods, referencing the new metasig declarations.
+
+**********/
+
// METHOD__STRING__CTORF_XXX has to be in same order as ECall::CtorCharXxx
#define METHOD__STRING__CTORF_FIRST METHOD__STRING__CTORF_CHARARRAY
static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 0 == METHOD__STRING__CTORF_CHARARRAY);
@@ -55,14 +85,38 @@ static_assert_no_msg(ECallCtor_First + 8 == ECall::CtorSBytePtrStartLengthEncodi
#define NumberOfStringConstructors 9
+#ifdef FEATURE_UTF8STRING
+// METHOD__UTF8STRING__CTORF_XXX has to be in same order as ECall::Utf8StringCtorCharXxx
+#define METHOD__UTF8STRING__CTORF_FIRST METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 0 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 1 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFCHAR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 2 == METHOD__UTF8_STRING__CTORF_BYTEARRAY_START_LEN);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 3 == METHOD__UTF8_STRING__CTORF_BYTEPTR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 4 == METHOD__UTF8_STRING__CTORF_CHARARRAY_START_LEN);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 5 == METHOD__UTF8_STRING__CTORF_CHARPTR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 6 == METHOD__UTF8_STRING__CTORF_STRING);
+
+// ECall::Utf8StringCtorCharXxx has to be in same order as METHOD__UTF8STRING__CTORF_XXX
+#define ECallUtf8String_Ctor_First ECall::Utf8StringCtorReadOnlySpanOfByteManaged
+static_assert_no_msg(ECallUtf8String_Ctor_First + 0 == ECall::Utf8StringCtorReadOnlySpanOfByteManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 1 == ECall::Utf8StringCtorReadOnlySpanOfCharManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 2 == ECall::Utf8StringCtorByteArrayStartLengthManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 3 == ECall::Utf8StringCtorBytePtrManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 4 == ECall::Utf8StringCtorCharArrayStartLengthManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 5 == ECall::Utf8StringCtorCharPtrManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 6 == ECall::Utf8StringCtorStringManaged);
+
+#define NumberOfUtf8StringConstructors 7
+#endif // FEATURE_UTF8STRING
+
void ECall::PopulateManagedStringConstructors()
{
STANDARD_VM_CONTRACT;
INDEBUG(static bool fInitialized = false);
_ASSERTE(!fInitialized); // assume this method is only called once
- _ASSERTE(g_pStringClass != NULL);
+ _ASSERTE(g_pStringClass != NULL);
for (int i = 0; i < NumberOfStringConstructors; i++)
{
MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__STRING__CTORF_FIRST + i));
@@ -72,6 +126,20 @@ void ECall::PopulateManagedStringConstructors()
ECall::DynamicallyAssignFCallImpl(pDest, ECallCtor_First + i);
}
+
+#ifdef FEATURE_UTF8STRING
+ _ASSERTE(g_pUtf8StringClass != NULL);
+ for (int i = 0; i < NumberOfUtf8StringConstructors; i++)
+ {
+ MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__UTF8STRING__CTORF_FIRST + i));
+ _ASSERTE(pMD != NULL);
+
+ PCODE pDest = pMD->GetMultiCallableAddrOfCode();
+
+ ECall::DynamicallyAssignFCallImpl(pDest, ECallUtf8String_Ctor_First + i);
+ }
+#endif // FEATURE_UTF8STRING
+
INDEBUG(fInitialized = true);
}
diff --git a/src/vm/ecall.h b/src/vm/ecall.h
index c809109c4c..58b4f0c34e 100644
--- a/src/vm/ecall.h
+++ b/src/vm/ecall.h
@@ -103,7 +103,7 @@ class ECall
static void EnumFCallMethods();
#endif // DACCESS_COMPILE
-#define DYNAMICALLY_ASSIGNED_FCALLS() \
+#define _DYNAMICALLY_ASSIGNED_FCALLS_BASE() \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateString, FramedAllocateString) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharArrayManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharArrayStartLengthManaged, NULL) \
@@ -116,6 +116,22 @@ class ECall
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthEncodingManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread, NULL) \
+#define _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING() \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateUtf8String, FramedAllocateUtf8String) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfByteManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfCharManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorByteArrayStartLengthManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorBytePtrManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharArrayStartLengthManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharPtrManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorStringManaged, NULL) \
+
+#ifdef FEATURE_UTF8STRING
+#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE() _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING()
+#else
+#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE()
+#endif // FEATURE_UTF8STRING
+
enum
{
#undef DYNAMICALLY_ASSIGNED_FCALL_IMPL
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index b4f9895301..7302bb4e4a 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -116,6 +116,19 @@ FCFuncStart(gStringFuncs)
FCFuncElement("Intern", AppDomainNative::GetOrInternString)
FCFuncEnd()
+#ifdef FEATURE_UTF8STRING
+FCFuncStart(gUtf8StringFuncs)
+ FCDynamic("FastAllocate", CORINFO_INTRINSIC_Illegal, ECall::FastAllocateUtf8String)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfByteManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfCharManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrByte_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorByteArrayStartLengthManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorBytePtrManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharArrayStartLengthManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharPtrManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Str_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorStringManaged)
+FCFuncEnd()
+#endif // FEATURE_UTF8STRING
+
FCFuncStart(gValueTypeFuncs)
FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits)
FCFuncElement("FastEqualsCheck", ValueTypeHelper::FastEqualsCheck)
@@ -526,8 +539,8 @@ FCFuncStart(gLoaderAllocatorFuncs)
FCFuncEnd()
FCFuncStart(gAssemblyFuncs)
- QCFuncElement("GetEntryAssembly", AssemblyNative::GetEntryAssembly)
- QCFuncElement("GetExecutingAssembly", AssemblyNative::GetExecutingAssembly)
+ QCFuncElement("GetEntryAssemblyNative", AssemblyNative::GetEntryAssembly)
+ QCFuncElement("GetExecutingAssemblyNative", AssemblyNative::GetExecutingAssembly)
FCFuncEnd()
FCFuncStart(gAssemblyBuilderFuncs)
@@ -1270,6 +1283,9 @@ FCClassElement("TypedReference", "System", gTypedReferenceFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("UriMarshaler", "System.StubHelpers", gUriMarshalerFuncs)
#endif
+#ifdef FEATURE_UTF8STRING
+FCClassElement("Utf8String", "System", gUtf8StringFuncs)
+#endif // FEATURE_UTF8STRING
FCClassElement("ValueClassMarshaler", "System.StubHelpers", gValueClassMarshalerFuncs)
FCClassElement("ValueType", "System", gValueTypeFuncs)
#ifdef FEATURE_COMINTEROP
diff --git a/src/vm/eepolicy.cpp b/src/vm/eepolicy.cpp
index bba511a275..a6ecda0520 100644
--- a/src/vm/eepolicy.cpp
+++ b/src/vm/eepolicy.cpp
@@ -1194,10 +1194,26 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE
UNREACHABLE();
}
+#if defined(_TARGET_X86_) && defined(PLATFORM_WINDOWS)
+// This noinline method is required to ensure that RtlCaptureContext captures
+// the context of HandleFatalError. On x86 RtlCaptureContext will not capture
+// the current method's context
+// NOTE: explicitly turning off optimizations to force the compiler to spill to the
+// stack and establish a stack frame. This is required to ensure that
+// RtlCaptureContext captures the context of HandleFatalError
+#pragma optimize("", off)
+int NOINLINE WrapperClrCaptureContext(CONTEXT* context)
+{
+ ClrCaptureContext(context);
+ return 0;
+}
+#pragma optimize("", on)
+#endif // defined(_TARGET_X86_) && defined(PLATFORM_WINDOWS)
-
-
-void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */, LPCWSTR errorSource /* = NULL */, LPCWSTR argExceptionString /* = NULL */)
+// This method must return a value to avoid getting non-actionable dumps on x86.
+// If this method were a DECLSPEC_NORETURN then dumps would not provide the necessary
+// context at the point of the failure
+int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */, LPCWSTR errorSource /* = NULL */, LPCWSTR argExceptionString /* = NULL */)
{
WRAPPER_NO_CONTRACT;
@@ -1215,7 +1231,12 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR addres
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_CONTROL;
+#if defined(_TARGET_X86_) && defined(PLATFORM_WINDOWS)
+ // Add a frame to ensure that the context captured is this method and not the caller
+ WrapperClrCaptureContext(&context);
+#else // defined(_TARGET_X86_) && defined(PLATFORM_WINDOWS)
ClrCaptureContext(&context);
+#endif
exceptionRecord.ExceptionCode = exitCode;
exceptionRecord.ExceptionAddress = reinterpret_cast< PVOID >(address);
@@ -1269,6 +1290,7 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR addres
}
UNREACHABLE();
+ return -1;
}
void EEPolicy::HandleExitProcessFromEscalation(EPolicyAction action, UINT exitCode)
diff --git a/src/vm/eepolicy.h b/src/vm/eepolicy.h
index dc997db4d2..a87527ce80 100644
--- a/src/vm/eepolicy.h
+++ b/src/vm/eepolicy.h
@@ -120,7 +120,7 @@ public:
static void HandleExitProcess(ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete);
- static void DECLSPEC_NORETURN HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL, LPCWSTR errorSource=NULL, LPCWSTR argExceptionString=NULL);
+ static int NOINLINE HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL, LPCWSTR errorSource=NULL, LPCWSTR argExceptionString=NULL);
static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE);
diff --git a/src/vm/encee.cpp b/src/vm/encee.cpp
index 7291256217..a13e3bf232 100644
--- a/src/vm/encee.cpp
+++ b/src/vm/encee.cpp
@@ -683,6 +683,7 @@ HRESULT EditAndContinueModule::ResumeInUpdatedFunction(
// If we fail for any reason we have already potentially trashed with new locals and we have also unwound any
// Win32 handlers on the stack so cannot ever return from this function.
EEPOLICY_HANDLE_FATAL_ERROR(CORDBG_E_ENC_INTERNAL_ERROR);
+ return hr;
}
//---------------------------------------------------------------------------------------
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 4de0b3c943..634f1d0d69 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -8,6 +8,7 @@
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
#include "eventpipeconfiguration.h"
+#include "eventpipesessionprovider.h"
#include "eventpipeevent.h"
#include "eventpipeeventsource.h"
#include "eventpipefile.h"
@@ -26,12 +27,12 @@
CrstStatic EventPipe::s_configCrst;
bool EventPipe::s_tracingInitialized = false;
-EventPipeConfiguration* EventPipe::s_pConfig = NULL;
-EventPipeSession* EventPipe::s_pSession = NULL;
-EventPipeBufferManager* EventPipe::s_pBufferManager = NULL;
+EventPipeConfiguration *EventPipe::s_pConfig = NULL;
+EventPipeSession *EventPipe::s_pSession = NULL;
+EventPipeBufferManager *EventPipe::s_pBufferManager = NULL;
LPCWSTR EventPipe::s_pOutputPath = NULL;
-EventPipeFile* EventPipe::s_pFile = NULL;
-EventPipeEventSource* EventPipe::s_pEventSource = NULL;
+EventPipeFile *EventPipe::s_pFile = NULL;
+EventPipeEventSource *EventPipe::s_pEventSource = NULL;
LPCWSTR EventPipe::s_pCommandLine = NULL;
unsigned long EventPipe::s_nextFileIndex;
HANDLE EventPipe::s_fileSwitchTimerHandle = NULL;
@@ -78,7 +79,7 @@ EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int
m_allocatedData = false;
S_UINT32 tmp_size = S_UINT32(0);
- for (unsigned int i=0; i<m_eventDataCount; i++)
+ for (unsigned int i = 0; i < m_eventDataCount; i++)
{
tmp_size += S_UINT32(m_pEventData[i].Size);
}
@@ -106,7 +107,7 @@ EventPipeEventPayload::~EventPipeEventPayload()
}
CONTRACTL_END;
- if(m_allocatedData && m_pData != NULL)
+ if (m_allocatedData && m_pData != NULL)
{
delete[] m_pData;
m_pData = NULL;
@@ -123,11 +124,11 @@ void EventPipeEventPayload::Flatten()
}
CONTRACTL_END;
- if(m_size > 0)
+ if (m_size > 0)
{
if (!IsFlattened())
{
- BYTE* tmp_pData = new (nothrow) BYTE[m_size];
+ BYTE *tmp_pData = new (nothrow) BYTE[m_size];
if (tmp_pData != NULL)
{
m_allocatedData = true;
@@ -148,26 +149,26 @@ void EventPipeEventPayload::CopyData(BYTE *pDst)
}
CONTRACTL_END;
- if(m_size > 0)
+ if (m_size > 0)
{
- if(IsFlattened())
+ if (IsFlattened())
{
memcpy(pDst, m_pData, m_size);
}
- else if(m_pEventData != NULL)
+ else if (m_pEventData != NULL)
{
unsigned int offset = 0;
- for(unsigned int i=0; i<m_eventDataCount; i++)
+ for (unsigned int i = 0; i < m_eventDataCount; i++)
{
- memcpy(pDst + offset, (BYTE*) m_pEventData[i].Ptr, m_pEventData[i].Size);
+ memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size);
offset += m_pEventData[i].Size;
}
}
}
}
-BYTE* EventPipeEventPayload::GetFlatData()
+BYTE *EventPipeEventPayload::GetFlatData()
{
CONTRACTL
{
@@ -222,7 +223,7 @@ void EventPipe::Shutdown()
{
Disable((EventPipeSessionID)s_pSession);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
// Save pointers to the configuration and buffer manager.
@@ -236,42 +237,48 @@ void EventPipe::Shutdown()
FlushProcessWriteBuffers();
// Free resources.
- delete(pConfig);
- delete(pBufferManager);
- delete(s_pEventSource);
+ delete pConfig;
+ delete pBufferManager;
+ delete s_pEventSource;
s_pEventSource = NULL;
- delete(s_pOutputPath);
+ delete[] s_pOutputPath;
s_pOutputPath = NULL;
// On Windows, this is just a pointer to the return value from
// GetCommandLineW(), so don't attempt to free it.
#ifdef FEATURE_PAL
- delete[](s_pCommandLine);
+ delete[] s_pCommandLine;
s_pCommandLine = NULL;
#endif
}
EventPipeSessionID EventPipe::Enable(
LPCWSTR strOutputPath,
- unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- int numProviders,
- UINT64 multiFileTraceLengthInSeconds)
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
+ // Take the lock before enabling tracing.
+ CrstHolder _crst(GetLock());
+
// Create a new session.
+ SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
EventPipeSession *pSession = s_pConfig->CreateSession(
(strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
circularBufferSizeInMB,
pProviders,
- static_cast<unsigned int>(numProviders),
+ numProviders,
multiFileTraceLengthInSeconds);
// Enable the session.
@@ -286,27 +293,21 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(pSession != NULL);
+ PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
// If tracing is not initialized or is already enabled, bail here.
- if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled())
- {
+ if (!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled())
return 0;
- }
// If the state or arguments are invalid, bail here.
- if(pSession == NULL || !pSession->IsValid())
- {
+ if (pSession == NULL || !pSession->IsValid())
return 0;
- }
// Enable the EventPipe EventSource.
s_pEventSource->Enable(pSession);
- // Take the lock before enabling tracing.
- CrstHolder _crst(GetLock());
-
// Initialize the next file index.
s_nextFileIndex = 1;
@@ -342,7 +343,7 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
SampleProfiler::Enable();
// Enable the file switch timer if needed.
- if(s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
+ if (s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
{
CreateFileSwitchTimer();
}
@@ -363,7 +364,7 @@ void EventPipe::Disable(EventPipeSessionID id)
// Only perform the disable operation if the session ID
// matches the current active session.
- if(id != (EventPipeSessionID)s_pSession)
+ if (id != (EventPipeSessionID)s_pSession)
{
return;
}
@@ -374,7 +375,7 @@ void EventPipe::Disable(EventPipeSessionID id)
// Take the lock before disabling tracing.
CrstHolder _crst(GetLock());
- if(s_pConfig != NULL && s_pConfig->Enabled())
+ if (s_pConfig != NULL && s_pConfig->Enabled())
{
// Disable the profiler.
SampleProfiler::Disable();
@@ -399,30 +400,31 @@ void EventPipe::Disable(EventPipeSessionID id)
FlushProcessWriteBuffers();
// Write to the file.
- if(s_pFile != NULL)
+ if (s_pFile != NULL)
{
LARGE_INTEGER disableTimeStamp;
QueryPerformanceCounter(&disableTimeStamp);
s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
- if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
{
// Before closing the file, do rundown.
- const unsigned int numRundownProviders = 2;
- EventPipeProviderConfiguration rundownProviders[] =
- {
- { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL }, // Public provider.
- { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL } // Rundown provider.
+ const EventPipeProviderConfiguration RundownProviders[] = {
+ {W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL}, // Public provider.
+ {W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider.
};
+
// The circular buffer size doesn't matter because all events are written synchronously during rundown.
- s_pSession = s_pConfig->CreateSession(EventPipeSessionType::File, 1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders);
+ s_pSession = s_pConfig->CreateSession(
+ EventPipeSessionType::File,
+ 1 /* circularBufferSizeInMB */,
+ RundownProviders,
+ sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration));
s_pConfig->EnableRundown(s_pSession);
// Ask the runtime to emit rundown events.
- if(g_fEEStarted && !g_fEEShutDown)
- {
+ if (g_fEEStarted && !g_fEEShutDown)
ETW::EnumerationLog::EndRundown();
- }
// Disable the event pipe now that rundown is complete.
s_pConfig->Disable(s_pSession);
@@ -432,7 +434,7 @@ void EventPipe::Disable(EventPipeSessionID id)
s_pSession = NULL;
}
- delete(s_pFile);
+ delete s_pFile;
s_pFile = NULL;
}
@@ -456,7 +458,7 @@ void EventPipe::CreateFileSwitchTimer()
}
CONTRACTL_END
- NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext();
+ NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
if (timerContextHolder == NULL)
{
return;
@@ -503,7 +505,7 @@ void EventPipe::DeleteFileSwitchTimer()
}
CONTRACTL_END
- if((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
+ if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
{
s_fileSwitchTimerHandle = NULL;
}
@@ -520,25 +522,23 @@ void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN ti
}
CONTRACTL_END;
-
// Take the lock control lock to make sure that tracing isn't disabled during this operation.
CrstHolder _crst(GetLock());
// Make sure that we should actually switch files.
UINT64 multiFileTraceLengthInSeconds = s_pSession->GetMultiFileTraceLengthInSeconds();
- if(!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0)
+ if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0)
{
return;
}
GCX_PREEMP();
- if(CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000)))
+ if (CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000)))
{
SwitchToNextFile();
s_lastFileSwitchTime = CLRGetTickCount64();
}
-
}
void EventPipe::SwitchToNextFile()
@@ -562,14 +562,15 @@ void EventPipe::SwitchToNextFile()
// Open the new file.
SString nextTraceFilePath;
GetNextFilePath(s_pSession, nextTraceFilePath);
- EventPipeFile* pFile = new (nothrow) EventPipeFile(nextTraceFilePath);
- if(pFile == NULL)
+ EventPipeFile *pFile = new (nothrow) EventPipeFile(nextTraceFilePath);
+ if (pFile == NULL)
{
+ // TODO: Add error handling.
return;
}
// Close the previous file.
- delete(s_pFile);
+ delete s_pFile;
// Swap in the new file.
s_pFile = pFile;
@@ -592,11 +593,11 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi
// If multiple files have been requested, then add a sequence number to the trace file name.
UINT64 multiFileTraceLengthInSeconds = pSession->GetMultiFileTraceLengthInSeconds();
- if(multiFileTraceLengthInSeconds > 0)
+ if (multiFileTraceLengthInSeconds > 0)
{
// Remove the ".netperf" file extension if it exists.
SString::Iterator netPerfExtension = nextTraceFilePath.End();
- if(nextTraceFilePath.FindBack(netPerfExtension, W(".netperf")))
+ if (nextTraceFilePath.FindBack(netPerfExtension, W(".netperf")))
{
nextTraceFilePath.Truncate(netPerfExtension);
}
@@ -608,12 +609,12 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi
}
}
-EventPipeSession* EventPipe::GetSession(EventPipeSessionID id)
+EventPipeSession *EventPipe::GetSession(EventPipeSessionID id)
{
LIMITED_METHOD_CONTRACT;
EventPipeSession *pSession = NULL;
- if((EventPipeSessionID)s_pSession == id)
+ if ((EventPipeSessionID)s_pSession == id)
{
pSession = s_pSession;
}
@@ -625,7 +626,7 @@ bool EventPipe::Enabled()
LIMITED_METHOD_CONTRACT;
bool enabled = false;
- if(s_pConfig != NULL)
+ if (s_pConfig != NULL)
{
enabled = s_pConfig->Enabled();
}
@@ -633,7 +634,7 @@ bool EventPipe::Enabled()
return enabled;
}
-EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -650,10 +651,9 @@ EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventP
}
return pProvider;
-
}
-EventPipeProvider* EventPipe::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
{
CONTRACTL
{
@@ -687,9 +687,9 @@ void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
// where we hold a provider after tracing has been disabled.
CrstHolder _crst(GetLock());
- if(pProvider != NULL)
+ if (pProvider != NULL)
{
- if(Enabled())
+ if (Enabled())
{
// Save the provider until the end of the tracing session.
pProvider->SetDeleteDeferred();
@@ -744,7 +744,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
CONTRACTL_END;
// Exit early if the event is not enabled.
- if(!event.IsEnabled())
+ if (!event.IsEnabled())
{
return;
}
@@ -752,7 +752,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
// Get the current thread;
Thread *pThread = GetThread();
- if(s_pConfig == NULL)
+ if (s_pConfig == NULL)
{
// We can't procede without a configuration
return;
@@ -760,23 +760,23 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
_ASSERTE(s_pSession != NULL);
// If the activity id isn't specified AND we are in a managed thread, pull it from the current thread.
- // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
- if(pActivityId == NULL && pThread != NULL)
+ // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
+ if (pActivityId == NULL && pThread != NULL)
{
pActivityId = pThread->GetActivityId();
}
- if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
+ if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
{
s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId);
}
- else if(s_pConfig->RundownEnabled())
+ else if (s_pConfig->RundownEnabled())
{
// It is possible that some events that are enabled on rundown can be emitted from other threads.
// We're not interested in these events and they can cause corrupted trace files because rundown
// events are written synchronously and not under lock.
// If we encounter an event that did not originate on the thread that is doing rundown, ignore it.
- if(pThread == NULL || !s_pConfig->IsRundownThread(pThread))
+ if (pThread == NULL || !s_pConfig->IsRundownThread(pThread))
{
return;
}
@@ -800,7 +800,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
pRelatedActivityId);
instance.EnsureStack(*s_pSession);
- if(s_pFile != NULL)
+ if (s_pFile != NULL)
{
// EventPipeFile::WriteEvent needs to allocate a metadata event
// and can therefore throw. In this context we will silently
@@ -809,7 +809,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
{
s_pFile->WriteEvent(instance);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
}
}
@@ -829,7 +829,7 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent
EventPipeEventPayload payload(pData, length);
// Write the event to the thread's buffer.
- if(s_pBufferManager != NULL)
+ if (s_pBufferManager != NULL)
{
// Specify the sampling thread as the "current thread", so that we select the right buffer.
// Specify the target thread so that the event gets properly attributed.
@@ -848,7 +848,7 @@ bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
CONTRACTL_END;
Thread *pThread = GetThread();
- if(pThread != NULL)
+ if (pThread != NULL)
{
return WalkManagedStackForThread(pThread, stackContents);
}
@@ -869,12 +869,12 @@ bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackC
// Calling into StackWalkFrames in preemptive mode violates the host contract,
// but this contract is not used on CoreCLR.
- CONTRACT_VIOLATION( HostViolation );
+ CONTRACT_VIOLATION(HostViolation);
stackContents.Reset();
StackWalkAction swaRet = pThread->StackWalkFrames(
- (PSTACKWALKFRAMESCALLBACK) &StackWalkCallback,
+ (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback,
&stackContents,
ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
@@ -895,9 +895,9 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
// Get the IP.
UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC;
- if(controlPC == 0)
+ if (controlPC == 0)
{
- if(pData->GetLength() == 0)
+ if (pData->GetLength() == 0)
{
// This happens for pinvoke stubs on the top of the stack.
return SWA_CONTINUE;
@@ -909,21 +909,20 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
// Add the IP to the captured stack.
pData->Append(
controlPC,
- pCf->GetFunction()
- );
+ pCf->GetFunction());
// Continue the stack walk.
return SWA_CONTINUE;
}
-EventPipeConfiguration* EventPipe::GetConfiguration()
+EventPipeConfiguration *EventPipe::GetConfiguration()
{
LIMITED_METHOD_CONTRACT;
return s_pConfig;
}
-CrstStatic* EventPipe::GetLock()
+CrstStatic *EventPipe::GetLock()
{
LIMITED_METHOD_CONTRACT;
@@ -955,7 +954,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv
commandLine.Append((WCHAR)' ');
commandLine.Append(pwzAssemblyPath);
- for(int i=0; i<argc; i++)
+ for (int i = 0; i < argc; i++)
{
commandLine.Append((WCHAR)' ');
commandLine.Append(argv[i]);
@@ -971,7 +970,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv
#endif
}
-EventPipeEventInstance* EventPipe::GetNextEvent()
+EventPipeEventInstance *EventPipe::GetNextEvent()
{
CONTRACTL
{
@@ -993,253 +992,4 @@ EventPipeEventInstance* EventPipe::GetNextEvent()
return pInstance;
}
-UINT64 QCALLTYPE EventPipeInternal::Enable(
- __in_z LPCWSTR outputFile,
- UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
- EventPipeProviderConfiguration *pProviders,
- INT32 numProviders,
- UINT64 multiFileTraceLengthInSeconds)
-{
- QCALL_CONTRACT;
-
- UINT64 sessionID = 0;
-
- BEGIN_QCALL;
- SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
- sessionID = EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds);
- END_QCALL;
-
- return sessionID;
-}
-
-void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
- EventPipe::Disable(sessionID);
- END_QCALL;
-}
-
-bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo)
-{
- QCALL_CONTRACT;
-
- bool retVal = false;
- BEGIN_QCALL;
-
- if(pSessionInfo != NULL)
- {
- EventPipeSession *pSession = EventPipe::GetSession(sessionID);
- if(pSession != NULL)
- {
- pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime();
- pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart;
- QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency);
- retVal = true;
- }
- }
-
- END_QCALL;
- return retVal;
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
- __in_z LPCWSTR providerName,
- EventPipeCallback pCallbackFunc)
-{
- QCALL_CONTRACT;
-
- EventPipeProvider *pProvider = NULL;
-
- BEGIN_QCALL;
-
- pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pProvider);
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::DefineEvent(
- INT_PTR provHandle,
- UINT32 eventID,
- __int64 keywords,
- UINT32 eventVersion,
- UINT32 level,
- void *pMetadata,
- UINT32 metadataLength)
-{
- QCALL_CONTRACT;
-
- EventPipeEvent *pEvent = NULL;
-
- BEGIN_QCALL;
-
- _ASSERTE(provHandle != NULL);
- EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
- pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength);
- _ASSERTE(pEvent != NULL);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pEvent);
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::GetProvider(
- __in_z LPCWSTR providerName)
-{
- QCALL_CONTRACT;
-
- EventPipeProvider *pProvider = NULL;
-
- BEGIN_QCALL;
-
- pProvider = EventPipe::GetProvider(providerName);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pProvider);
-}
-
-void QCALLTYPE EventPipeInternal::DeleteProvider(
- INT_PTR provHandle)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- if(provHandle != NULL)
- {
- EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider*>(provHandle);
- EventPipe::DeleteProvider(pProvider);
- }
-
- END_QCALL;
-}
-
-int QCALLTYPE EventPipeInternal::EventActivityIdControl(
- uint controlCode,
- GUID *pActivityId)
-{
-
- QCALL_CONTRACT;
-
- int retVal = 0;
-
- BEGIN_QCALL;
-
- Thread *pThread = GetThread();
- if(pThread == NULL || pActivityId == NULL)
- {
- retVal = 1;
- }
- else
- {
- ActivityControlCode activityControlCode = (ActivityControlCode)controlCode;
- GUID currentActivityId;
- switch(activityControlCode)
- {
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID:
-
- *pActivityId = *pThread->GetActivityId();
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID:
-
- pThread->SetActivityId(pActivityId);
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID:
-
- CoCreateGuid(pActivityId);
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID:
-
- currentActivityId = *pThread->GetActivityId();
- pThread->SetActivityId(pActivityId);
- *pActivityId = currentActivityId;
-
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID:
-
- *pActivityId = *pThread->GetActivityId();
- CoCreateGuid(&currentActivityId);
- pThread->SetActivityId(&currentActivityId);
- break;
-
- default:
- retVal = 1;
- };
- }
-
- END_QCALL;
- return retVal;
-}
-
-void QCALLTYPE EventPipeInternal::WriteEvent(
- INT_PTR eventHandle,
- UINT32 eventID,
- void *pData,
- UINT32 length,
- LPCGUID pActivityId,
- LPCGUID pRelatedActivityId)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- _ASSERTE(eventHandle != NULL);
- EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
- EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId);
-
- END_QCALL;
-}
-
-void QCALLTYPE EventPipeInternal::WriteEventData(
- INT_PTR eventHandle,
- UINT32 eventID,
- EventData *pEventData,
- UINT32 eventDataCount,
- LPCGUID pActivityId,
- LPCGUID pRelatedActivityId)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- _ASSERTE(eventHandle != NULL);
- EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
- EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId);
-
- END_QCALL;
-}
-
-bool QCALLTYPE EventPipeInternal::GetNextEvent(
- EventPipeEventInstanceData *pInstance)
-{
- QCALL_CONTRACT;
-
- EventPipeEventInstance *pNextInstance = NULL;
- BEGIN_QCALL;
-
- _ASSERTE(pInstance != NULL);
-
- pNextInstance = EventPipe::GetNextEvent();
- if (pNextInstance)
- {
- pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider();
- pInstance->EventID = pNextInstance->GetEvent()->GetEventID();
- pInstance->ThreadID = pNextInstance->GetThreadId();
- pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart;
- pInstance->ActivityId = *pNextInstance->GetActivityId();
- pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId();
- pInstance->Payload = pNextInstance->GetData();
- pInstance->PayloadLength = pNextInstance->GetDataLength();
- }
-
- END_QCALL;
- return pNextInstance != NULL;
-}
-
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index c77b94dbdc..494a8c5c98 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -14,8 +14,6 @@ class EventPipeConfiguration;
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeFile;
-class EventPipeJsonFile;
-class EventPipeBuffer;
class EventPipeBufferManager;
class EventPipeEventSource;
class EventPipeProvider;
@@ -35,13 +33,13 @@ struct EventFilterDescriptor
ULONGLONG Ptr;
// The size of the filter data, in bytes. The maximum size is 1024 bytes.
- ULONG Size;
+ ULONG Size;
// The type of filter data. The type is application-defined. An event
// controller that knows about the provider and knows details about the
// provider's events can use the Type field to send the provider an
// arbitrary set of data for use as enhancements to the filtering of events.
- ULONG Type;
+ ULONG Type;
};
// Define the event pipe callback to match the ETW callback signature.
@@ -56,7 +54,6 @@ typedef void (*EventPipeCallback)(
struct EventData
{
-public:
UINT64 Ptr;
unsigned int Size;
unsigned int Reserved;
@@ -91,7 +88,7 @@ public:
// Get the flat formatted data in this payload
// This method will allocate a buffer if it does not already contain flattened data
// This method will return NULL on OOM if a buffer needed to be allocated
- BYTE* GetFlatData();
+ BYTE *GetFlatData();
// Return true is the data is stored in a flat buffer
bool IsFlattened() const
@@ -109,7 +106,7 @@ public:
return m_size;
}
- EventData* GetEventDataArray() const
+ EventData *GetEventDataArray() const
{
LIMITED_METHOD_CONTRACT;
@@ -120,7 +117,6 @@ public:
class StackContents
{
private:
-
const static unsigned int MAX_STACK_DEPTH = 100;
// Array of IP values from a stack crawl.
@@ -130,14 +126,13 @@ private:
#ifdef _DEBUG
// Parallel array of MethodDesc pointers.
// Used for debug-only stack printing.
- MethodDesc* m_methods[MAX_STACK_DEPTH];
+ MethodDesc *m_methods[MAX_STACK_DEPTH];
#endif // _DEBUG
// The next available slot in StackFrames.
unsigned int m_nextAvailableFrame;
public:
-
StackContents()
{
LIMITED_METHOD_CONTRACT;
@@ -152,7 +147,7 @@ public:
memcpy_s(pDest->m_stackFrames, MAX_STACK_DEPTH * sizeof(UINT_PTR), m_stackFrames, sizeof(UINT_PTR) * m_nextAvailableFrame);
#ifdef _DEBUG
- memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc*), m_methods, sizeof(MethodDesc*) * m_nextAvailableFrame);
+ memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc *), m_methods, sizeof(MethodDesc *) * m_nextAvailableFrame);
#endif
pDest->m_nextAvailableFrame = m_nextAvailableFrame;
}
@@ -192,7 +187,7 @@ public:
}
#ifdef _DEBUG
- MethodDesc* GetMethod(unsigned int frameIndex)
+ MethodDesc *GetMethod(unsigned int frameIndex)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(frameIndex < MAX_STACK_DEPTH);
@@ -210,7 +205,7 @@ public:
{
LIMITED_METHOD_CONTRACT;
- if(m_nextAvailableFrame < MAX_STACK_DEPTH)
+ if (m_nextAvailableFrame < MAX_STACK_DEPTH)
{
m_stackFrames[m_nextAvailableFrame] = controlPC;
#ifdef _DEBUG
@@ -220,11 +215,11 @@ public:
}
}
- BYTE* GetPointer() const
+ BYTE *GetPointer() const
{
LIMITED_METHOD_CONTRACT;
- return (BYTE*)m_stackFrames;
+ return (BYTE *)m_stackFrames;
}
unsigned int GetSize() const
@@ -246,146 +241,128 @@ class EventPipe
friend class EventPipeBufferManager;
friend class SampleProfiler;
- public:
-
- // Initialize the event pipe.
- static void Initialize();
-
- // Shutdown the event pipe.
- static void Shutdown();
-
- // Enable tracing via the event pipe.
- static EventPipeSessionID Enable(
- LPCWSTR strOutputPath,
- unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- int numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
- // Disable tracing via the event pipe.
- static void Disable(EventPipeSessionID id);
-
- // Get the session for the specified session ID.
- static EventPipeSession* GetSession(EventPipeSessionID id);
-
- // Specifies whether or not the event pipe is enabled.
- static bool Enabled();
-
- // Create a provider.
- static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
-
- // Get a provider.
- static EventPipeProvider* GetProvider(const SString &providerName);
-
- // Delete a provider.
- static void DeleteProvider(EventPipeProvider *pProvider);
-
- // Write out an event from a flat buffer.
- // Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
-
- // Write out an event from an EventData array.
- // Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
-
- // Write out a sample profile event.
- static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0);
-
- // Get the managed call stack for the current thread.
- static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
+public:
+ // Initialize the event pipe.
+ static void Initialize();
- // Get the managed call stack for the specified thread.
- static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
+ // Shutdown the event pipe.
+ static void Shutdown();
- // Save the command line for the current process.
- static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv);
+ // Enable tracing via the event pipe.
+ static EventPipeSessionID Enable(
+ LPCWSTR strOutputPath,
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds);
- // Get next event.
- static EventPipeEventInstance* GetNextEvent();
+ // Disable tracing via the event pipe.
+ static void Disable(EventPipeSessionID id);
- protected:
+ // Get the session for the specified session ID.
+ static EventPipeSession *GetSession(EventPipeSessionID id);
- // The counterpart to WriteEvent which after the payload is constructed
- static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+ // Specifies whether or not the event pipe is enabled.
+ static bool Enabled();
- private:
+ // Create a provider.
+ static EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
- // Enable the specified EventPipe session.
- static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession);
+ // Get a provider.
+ static EventPipeProvider *GetProvider(const SString &providerName);
- static void CreateFileSwitchTimer();
+ // Delete a provider.
+ static void DeleteProvider(EventPipeProvider *pProvider);
- static void DeleteFileSwitchTimer();
+ // Write out an event from a flat buffer.
+ // Data is written as a serialized blob matching the ETW serialization conventions.
+ static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
- // Performs one polling operation to determine if it is necessary to switch to a new file.
- // If the polling operation decides it is time, it will perform the switch.
- // Called directly from the timer when the timer is triggered.
- static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired);
+ // Write out an event from an EventData array.
+ // Data is written as a serialized blob matching the ETW serialization conventions.
+ static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
- // If event pipe has been configured to write multiple files, switch to the next file.
- static void SwitchToNextFile();
+ // Write out a sample profile event.
+ static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0);
- // Generate the file path for the next trace file.
- // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time.
- static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath);
+ // Get the managed call stack for the current thread.
+ static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
- // Callback function for the stack walker. For each frame walked, this callback is invoked.
- static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
+ // Get the managed call stack for the specified thread.
+ static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
- // Get the configuration object.
- // This is called directly by the EventPipeProvider constructor to register the new provider.
- static EventPipeConfiguration* GetConfiguration();
+ // Save the command line for the current process.
+ static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv);
- // Get the event pipe configuration lock.
- static CrstStatic* GetLock();
+ // Get next event.
+ static EventPipeEventInstance *GetNextEvent();
- static CrstStatic s_configCrst;
- static bool s_tracingInitialized;
- static EventPipeConfiguration *s_pConfig;
- static EventPipeSession *s_pSession;
- static EventPipeBufferManager *s_pBufferManager;
- static LPCWSTR s_pOutputPath;
- static unsigned long s_nextFileIndex;
- static EventPipeFile *s_pFile;
- static EventPipeEventSource *s_pEventSource;
- static LPCWSTR s_pCommandLine;
- const static DWORD FileSwitchTimerPeriodMS = 1000;
- static HANDLE s_fileSwitchTimerHandle;
- static ULONGLONG s_lastFileSwitchTime;
+private:
+ // The counterpart to WriteEvent which after the payload is constructed
+ static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+
+ // Enable the specified EventPipe session.
+ static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession);
+
+ static void CreateFileSwitchTimer();
+
+ static void DeleteFileSwitchTimer();
+
+ // Performs one polling operation to determine if it is necessary to switch to a new file.
+ // If the polling operation decides it is time, it will perform the switch.
+ // Called directly from the timer when the timer is triggered.
+ static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired);
+
+ // If event pipe has been configured to write multiple files, switch to the next file.
+ static void SwitchToNextFile();
+
+ // Generate the file path for the next trace file.
+ // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time.
+ static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath);
+
+ // Callback function for the stack walker. For each frame walked, this callback is invoked.
+ static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
+
+ // Get the configuration object.
+ // This is called directly by the EventPipeProvider constructor to register the new provider.
+ static EventPipeConfiguration *GetConfiguration();
+
+ // Get the event pipe configuration lock.
+ static CrstStatic *GetLock();
+
+ static CrstStatic s_configCrst;
+ static bool s_tracingInitialized;
+ static EventPipeConfiguration *s_pConfig;
+ static EventPipeSession *s_pSession;
+ static EventPipeBufferManager *s_pBufferManager;
+ static LPCWSTR s_pOutputPath;
+ static unsigned long s_nextFileIndex;
+ static EventPipeFile *s_pFile;
+ static EventPipeEventSource *s_pEventSource;
+ static LPCWSTR s_pCommandLine;
+ const static DWORD FileSwitchTimerPeriodMS = 1000;
+ static HANDLE s_fileSwitchTimerHandle;
+ static ULONGLONG s_lastFileSwitchTime;
};
struct EventPipeProviderConfiguration
{
-
private:
-
- LPCWSTR m_pProviderName;
- UINT64 m_keywords;
- UINT32 m_loggingLevel;
- LPCWSTR m_pFilterData;
+ LPCWSTR m_pProviderName = nullptr;
+ UINT64 m_keywords = 0;
+ UINT32 m_loggingLevel = 0;
+ LPCWSTR m_pFilterData = nullptr;
public:
+ EventPipeProviderConfiguration() = default;
- EventPipeProviderConfiguration()
+ EventPipeProviderConfiguration(LPCWSTR pProviderName, UINT64 keywords, UINT32 loggingLevel, LPCWSTR pFilterData) :
+ m_pProviderName(pProviderName),
+ m_keywords(keywords),
+ m_loggingLevel(loggingLevel),
+ m_pFilterData(pFilterData)
{
- LIMITED_METHOD_CONTRACT;
- m_pProviderName = NULL;
- m_keywords = NULL;
- m_loggingLevel = 0;
- m_pFilterData = NULL;
- }
-
- EventPipeProviderConfiguration(
- LPCWSTR pProviderName,
- UINT64 keywords,
- UINT32 loggingLevel,
- LPCWSTR pFilterData)
- {
- LIMITED_METHOD_CONTRACT;
- m_pProviderName = pProviderName;
- m_keywords = keywords;
- m_loggingLevel = loggingLevel;
- m_pFilterData = pFilterData;
}
LPCWSTR GetProviderName() const
@@ -413,95 +390,6 @@ public:
}
};
-class EventPipeInternal
-{
-private:
-
- enum class ActivityControlCode
- {
- EVENT_ACTIVITY_CONTROL_GET_ID = 1,
- EVENT_ACTIVITY_CONTROL_SET_ID = 2,
- EVENT_ACTIVITY_CONTROL_CREATE_ID = 3,
- EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4,
- EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
- };
-
- struct EventPipeEventInstanceData
- {
- public:
- void *ProviderID;
- unsigned int EventID;
- unsigned int ThreadID;
- LARGE_INTEGER TimeStamp;
- GUID ActivityId;
- GUID RelatedActivityId;
- const BYTE *Payload;
- unsigned int PayloadLength;
- };
-
- struct EventPipeSessionInfo
- {
- public:
- FILETIME StartTimeAsUTCFileTime;
- LARGE_INTEGER StartTimeStamp;
- LARGE_INTEGER TimeStampFrequency;
- };
-
-public:
-
- static UINT64 QCALLTYPE Enable(
- __in_z LPCWSTR outputFile,
- UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
- EventPipeProviderConfiguration *pProviders,
- INT32 numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
- static void QCALLTYPE Disable(UINT64 sessionID);
-
- static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo);
-
- static INT_PTR QCALLTYPE CreateProvider(
- __in_z LPCWSTR providerName,
- EventPipeCallback pCallbackFunc);
-
- static INT_PTR QCALLTYPE DefineEvent(
- INT_PTR provHandle,
- UINT32 eventID,
- __int64 keywords,
- UINT32 eventVersion,
- UINT32 level,
- void *pMetadata,
- UINT32 metadataLength);
-
- static INT_PTR QCALLTYPE GetProvider(
- __in_z LPCWSTR providerName);
-
- static void QCALLTYPE DeleteProvider(
- INT_PTR provHandle);
-
- static int QCALLTYPE EventActivityIdControl(
- uint controlCode,
- GUID *pActivityId);
-
- static void QCALLTYPE WriteEvent(
- INT_PTR eventHandle,
- UINT32 eventID,
- void *pData,
- UINT32 length,
- LPCGUID pActivityId, LPCGUID pRelatedActivityId);
-
- static void QCALLTYPE WriteEventData(
- INT_PTR eventHandle,
- UINT32 eventID,
- EventData *pEventData,
- UINT32 eventDataCount,
- LPCGUID pActivityId, LPCGUID pRelatedActivityId);
-
- static bool QCALLTYPE GetNextEvent(
- EventPipeEventInstanceData *pInstance);
-};
-
#endif // FEATURE_PERFTRACING
#endif // __EVENTPIPE_H__
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
index 9cf8280722..4667276753 100644
--- a/src/vm/eventpipeconfiguration.cpp
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -6,12 +6,13 @@
#include "eventpipe.h"
#include "eventpipeconfiguration.h"
#include "eventpipeeventinstance.h"
+#include "eventpipesessionprovider.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
#ifdef FEATURE_PERFTRACING
-const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
+const WCHAR *EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
EventPipeConfiguration::EventPipeConfiguration()
{
@@ -22,7 +23,7 @@ EventPipeConfiguration::EventPipeConfiguration()
m_pRundownThread = NULL;
m_pConfigProvider = NULL;
m_pSession = NULL;
- m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
+ m_pProviderList = new SList<SListElem<EventPipeProvider *>>();
}
EventPipeConfiguration::~EventPipeConfiguration()
@@ -35,25 +36,25 @@ EventPipeConfiguration::~EventPipeConfiguration()
}
CONTRACTL_END;
- if(m_pConfigProvider != NULL)
+ if (m_pConfigProvider != NULL)
{
// This unregisters the provider, which takes a
// HOST_BREAKABLE lock
EX_TRY
{
- DeleteProvider(m_pConfigProvider);
- m_pConfigProvider = NULL;
+ DeleteProvider(m_pConfigProvider);
+ m_pConfigProvider = NULL;
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
}
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
DeleteSession(m_pSession);
m_pSession = NULL;
}
- if(m_pProviderList != NULL)
+ if (m_pProviderList != NULL)
{
// We swallow exceptions here because the HOST_BREAKABLE
// lock may throw and this destructor gets called in throw
@@ -63,18 +64,18 @@ EventPipeConfiguration::~EventPipeConfiguration()
// Take the lock before manipulating the list.
CrstHolder _crst(EventPipe::GetLock());
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
// We don't delete provider itself because it can be in-use
- SListElem<EventPipeProvider*> *pCurElem = pElem;
+ SListElem<EventPipeProvider *> *pCurElem = pElem;
pElem = m_pProviderList->GetNext(pElem);
- delete(pCurElem);
+ delete (pCurElem);
}
- delete(m_pProviderList);
+ delete (m_pProviderList);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
m_pProviderList = NULL;
@@ -96,14 +97,14 @@ void EventPipeConfiguration::Initialize()
// Create the metadata event.
m_pMetadataEvent = m_pConfigProvider->AddEvent(
- 0, /* eventID */
- 0, /* keywords */
- 0, /* eventVersion */
+ 0, /* eventID */
+ 0, /* keywords */
+ 0, /* eventVersion */
EventPipeEventLevel::LogAlways,
false); /* needStack */
}
-EventPipeProvider* EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider *EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -134,18 +135,15 @@ void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider)
CONTRACTL_END;
if (pProvider == NULL)
- {
return;
- }
// Unregister the provider.
UnregisterProvider(*pProvider);
// Free the provider itself.
- delete(pProvider);
+ delete pProvider;
}
-
bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
{
CONTRACTL
@@ -161,7 +159,7 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
// See if we've already registered this provider.
EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName());
- if(pExistingProvider != NULL)
+ if (pExistingProvider != NULL)
{
return false;
}
@@ -170,14 +168,14 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
if (m_pProviderList != NULL)
{
// The provider has not been registered, so register it.
- m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
+ m_pProviderList->InsertTail(new SListElem<EventPipeProvider *>(&provider));
}
// Set the provider configuration and enable it if it has been requested by a session.
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, &provider);
- if(pSessionProvider != NULL)
+ if (pSessionProvider != NULL)
{
provider.SetConfiguration(
true /* providerEnabled */,
@@ -207,10 +205,10 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
if (m_pProviderList != NULL)
{
// Find the provider.
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
- if(pElem->GetValue() == &provider)
+ if (pElem->GetValue() == &provider)
{
break;
}
@@ -219,11 +217,11 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
}
// If we found the provider, remove it.
- if(pElem != NULL)
+ if (pElem != NULL)
{
- if(m_pProviderList->FindAndRemove(pElem) != NULL)
+ if (m_pProviderList->FindAndRemove(pElem) != NULL)
{
- delete(pElem);
+ delete (pElem);
return true;
}
}
@@ -232,7 +230,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
return false;
}
-EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProvider(const SString &providerName)
{
CONTRACTL
{
@@ -249,7 +247,7 @@ EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerNa
return GetProviderNoLock(providerName);
}
-EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
{
CONTRACTL
{
@@ -263,11 +261,11 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
- if(pProvider->GetProviderName().Equals(providerName))
+ if (pProvider->GetProviderName().Equals(providerName))
{
return pProvider;
}
@@ -279,7 +277,7 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov
return NULL;
}
-EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
+EventPipeSessionProvider *EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
{
CONTRACTL
{
@@ -291,9 +289,9 @@ EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSe
CONTRACTL_END;
EventPipeSessionProvider *pRet = NULL;
- if(pSession != NULL)
+ if (pSession != NULL)
{
- pRet = pSession->GetSessionProvider(pProvider);
+ pRet = pSession->GetSessionProvider(pProvider);
}
return pRet;
}
@@ -303,24 +301,35 @@ size_t EventPipeConfiguration::GetCircularBufferSize() const
LIMITED_METHOD_CONTRACT;
size_t ret = 0;
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
ret = m_pSession->GetCircularBufferSize();
}
return ret;
}
-EventPipeSession* EventPipeConfiguration::CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds)
+EventPipeSession *EventPipeConfiguration::CreateSession(
+ EventPipeSessionType sessionType,
+ unsigned int circularBufferSizeInMB,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
- return new EventPipeSession(sessionType, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds);
+ return new EventPipeSession(
+ sessionType,
+ circularBufferSizeInMB,
+ pProviders,
+ numProviders,
+ multiFileTraceLengthInSeconds);
}
void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
@@ -336,9 +345,9 @@ void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
CONTRACTL_END;
// TODO: Multiple session support will require individual enabled bits.
- if(pSession != NULL && !m_enabled)
+ if (pSession != NULL && !m_enabled)
{
- delete(pSession);
+ delete (pSession);
}
}
@@ -361,14 +370,14 @@ void EventPipeConfiguration::Enable(EventPipeSession *pSession)
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
// Enable the provider if it has been configured.
EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, pProvider);
- if(pSessionProvider != NULL)
+ if (pSessionProvider != NULL)
{
pProvider->SetConfiguration(
true /* providerEnabled */,
@@ -400,8 +409,8 @@ void EventPipeConfiguration::Disable(EventPipeSession *pSession)
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
pProvider->SetConfiguration(
@@ -458,7 +467,7 @@ void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession)
Enable(pSession);
}
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
+EventPipeEventInstance *EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
{
CONTRACTL
{
@@ -490,7 +499,7 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
memcpy(currentPtr, &metadataId, sizeof(metadataId));
currentPtr += sizeof(metadataId);
- memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength);
+ memcpy(currentPtr, (BYTE *)providerName.GetUnicode(), providerNameLength);
currentPtr += providerNameLength;
// Write the incoming payload data.
@@ -523,19 +532,18 @@ void EventPipeConfiguration::DeleteDeferredProviders()
MODE_ANY;
// Lock must be held by EventPipe::Disable.
PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
-
}
CONTRACTL_END;
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
pElem = m_pProviderList->GetNext(pElem);
- if(pProvider->GetDeleteDeferred())
+ if (pProvider->GetDeleteDeferred())
{
DeleteProvider(pProvider);
}
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
index 5eec76ca80..6021305b82 100644
--- a/src/vm/eventpipeconfiguration.h
+++ b/src/vm/eventpipeconfiguration.h
@@ -13,10 +13,8 @@ class EventPipeSessionProvider;
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeProvider;
-struct EventPipeProviderConfiguration;
class EventPipeSession;
enum class EventPipeSessionType;
-class EventPipeSessionProvider;
enum class EventPipeEventLevel
{
@@ -31,7 +29,6 @@ enum class EventPipeEventLevel
class EventPipeConfiguration
{
public:
-
EventPipeConfiguration();
~EventPipeConfiguration();
@@ -39,7 +36,7 @@ public:
void Initialize();
// Create a new provider.
- EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData);
+ EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData);
// Delete a provider.
void DeleteProvider(EventPipeProvider *pProvider);
@@ -51,10 +48,15 @@ public:
bool UnregisterProvider(EventPipeProvider &provider);
// Get the provider with the specified provider ID if it exists.
- EventPipeProvider* GetProvider(const SString &providerID);
+ EventPipeProvider *GetProvider(const SString &providerID);
// Create a new session.
- EventPipeSession* CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds = 0);
+ EventPipeSession *CreateSession(
+ EventPipeSessionType sessionType,
+ unsigned int circularBufferSizeInMB,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds = 0);
// Delete a session.
void DeleteSession(EventPipeSession *pSession);
@@ -78,7 +80,7 @@ public:
void EnableRundown(EventPipeSession *pSession);
// Get the event used to write metadata to the event stream.
- EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId);
+ EventPipeEventInstance *BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId);
// Delete deferred providers.
void DeleteDeferredProviders();
@@ -93,12 +95,11 @@ public:
}
private:
-
// Get the provider without taking the lock.
- EventPipeProvider* GetProviderNoLock(const SString &providerID);
+ EventPipeProvider *GetProviderNoLock(const SString &providerID);
// Get the enabled provider.
- EventPipeSessionProvider* GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider);
+ EventPipeSessionProvider *GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider);
// The one and only EventPipe session.
EventPipeSession *m_pSession;
@@ -107,7 +108,7 @@ private:
Volatile<bool> m_enabled;
// The list of event pipe providers.
- SList<SListElem<EventPipeProvider*>> *m_pProviderList;
+ SList<SListElem<EventPipeProvider *>> *m_pProviderList;
// The provider used to write configuration events to the event stream.
EventPipeProvider *m_pConfigProvider;
@@ -117,7 +118,7 @@ private:
// The provider name for the configuration event pipe provider.
// This provider is used to emit configuration events.
- const static WCHAR* s_configurationProviderName;
+ const static WCHAR *s_configurationProviderName;
// True if rundown is enabled.
Volatile<bool> m_rundownEnabled;
diff --git a/src/vm/eventpipeeventinstance.h b/src/vm/eventpipeeventinstance.h
index 80fd49d946..cf995f2534 100644
--- a/src/vm/eventpipeeventinstance.h
+++ b/src/vm/eventpipeeventinstance.h
@@ -14,6 +14,8 @@
#include "fastserializableobject.h"
#include "fastserializer.h"
+class EventPipeJsonFile;
+
class EventPipeEventInstance
{
// Declare friends.
diff --git a/src/vm/eventpipeeventsource.cpp b/src/vm/eventpipeeventsource.cpp
index 20dc5b2686..20613b8d53 100644
--- a/src/vm/eventpipeeventsource.cpp
+++ b/src/vm/eventpipeeventsource.cpp
@@ -9,6 +9,7 @@
#include "eventpipemetadatagenerator.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
#ifdef FEATURE_PERFTRACING
diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp
index 424bbef5ad..2e98fd5ad7 100644
--- a/src/vm/eventpipefile.cpp
+++ b/src/vm/eventpipefile.cpp
@@ -11,8 +11,7 @@
#ifdef FEATURE_PERFTRACING
-EventPipeFile::EventPipeFile(
- SString &outputFilePath)
+EventPipeFile::EventPipeFile(SString &outputFilePath)
{
CONTRACTL
{
@@ -43,7 +42,7 @@ EventPipeFile::EventPipeFile(
m_samplingRateInNs = SampleProfiler::GetSamplingRate();
// Create the file stream and write the header.
- m_pSerializer = new FastSerializer(outputFilePath);
+ m_pSerializer = new FastSerializer(new FileStreamWriter(outputFilePath));
m_serializationLock.Init(LOCK_TYPE_DEFAULT);
m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>();
@@ -101,7 +100,7 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
metadataId = GenerateMetadataId();
EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId);
-
+
WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event.
SaveMetadataId(*instance.GetEvent(), metadataId);
@@ -129,7 +128,7 @@ void EventPipeFile::WriteEnd()
// "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read."
// see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more
- m_pSerializer->WriteTag(FastSerializerTags::NullReference);
+ m_pSerializer->WriteTag(FastSerializerTags::NullReference);
}
void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId)
diff --git a/src/vm/eventpipeinternal.cpp b/src/vm/eventpipeinternal.cpp
new file mode 100644
index 0000000000..c02317f491
--- /dev/null
+++ b/src/vm/eventpipeinternal.cpp
@@ -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.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipeinternal.h"
+#include "eventpipeprovider.h"
+#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
+
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+UINT64 QCALLTYPE EventPipeInternal::Enable(
+ __in_z LPCWSTR outputFile,
+ UINT32 circularBufferSizeInMB,
+ INT64 profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration *pProviders,
+ UINT32 numProviders,
+ UINT64 multiFileTraceLengthInSeconds)
+{
+ QCALL_CONTRACT;
+
+ UINT64 sessionID = 0;
+
+ BEGIN_QCALL;
+ {
+ sessionID = EventPipe::Enable(
+ outputFile,
+ circularBufferSizeInMB,
+ profilerSamplingRateInNanoseconds,
+ pProviders,
+ numProviders,
+ multiFileTraceLengthInSeconds);
+ }
+ END_QCALL;
+
+ return sessionID;
+}
+
+void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+ EventPipe::Disable(sessionID);
+ END_QCALL;
+}
+
+bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo)
+{
+ QCALL_CONTRACT;
+
+ bool retVal = false;
+ BEGIN_QCALL;
+
+ if (pSessionInfo != NULL)
+ {
+ EventPipeSession *pSession = EventPipe::GetSession(sessionID);
+ if (pSession != NULL)
+ {
+ pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime();
+ pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart;
+ QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency);
+ retVal = true;
+ }
+ }
+
+ END_QCALL;
+ return retVal;
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
+ __in_z LPCWSTR providerName,
+ EventPipeCallback pCallbackFunc)
+{
+ QCALL_CONTRACT;
+
+ EventPipeProvider *pProvider = NULL;
+
+ BEGIN_QCALL;
+
+ pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pProvider);
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::DefineEvent(
+ INT_PTR provHandle,
+ UINT32 eventID,
+ __int64 keywords,
+ UINT32 eventVersion,
+ UINT32 level,
+ void *pMetadata,
+ UINT32 metadataLength)
+{
+ QCALL_CONTRACT;
+
+ EventPipeEvent *pEvent = NULL;
+
+ BEGIN_QCALL;
+
+ _ASSERTE(provHandle != NULL);
+ EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
+ pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength);
+ _ASSERTE(pEvent != NULL);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pEvent);
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::GetProvider(__in_z LPCWSTR providerName)
+{
+ QCALL_CONTRACT;
+
+ EventPipeProvider *pProvider = NULL;
+
+ BEGIN_QCALL;
+
+ pProvider = EventPipe::GetProvider(providerName);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pProvider);
+}
+
+void QCALLTYPE EventPipeInternal::DeleteProvider(INT_PTR provHandle)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ if (provHandle != NULL)
+ {
+ EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
+ EventPipe::DeleteProvider(pProvider);
+ }
+
+ END_QCALL;
+}
+
+int QCALLTYPE EventPipeInternal::EventActivityIdControl(uint32_t controlCode, GUID *pActivityId)
+{
+
+ QCALL_CONTRACT;
+
+ int retVal = 0;
+
+ BEGIN_QCALL;
+
+ Thread *pThread = GetThread();
+ if (pThread == NULL || pActivityId == NULL)
+ {
+ retVal = 1;
+ }
+ else
+ {
+ ActivityControlCode activityControlCode = (ActivityControlCode)controlCode;
+ GUID currentActivityId;
+ switch (activityControlCode)
+ {
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID:
+
+ pThread->SetActivityId(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID:
+
+ CoCreateGuid(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID:
+
+ currentActivityId = *pThread->GetActivityId();
+ pThread->SetActivityId(pActivityId);
+ *pActivityId = currentActivityId;
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ CoCreateGuid(&currentActivityId);
+ pThread->SetActivityId(&currentActivityId);
+ break;
+
+ default:
+ retVal = 1;
+ }
+ }
+
+ END_QCALL;
+ return retVal;
+}
+
+void QCALLTYPE EventPipeInternal::WriteEvent(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ void *pData,
+ UINT32 length,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ _ASSERTE(eventHandle != NULL);
+ EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
+ EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId);
+
+ END_QCALL;
+}
+
+void QCALLTYPE EventPipeInternal::WriteEventData(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ EventData *pEventData,
+ UINT32 eventDataCount,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ _ASSERTE(eventHandle != NULL);
+ EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
+ EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId);
+
+ END_QCALL;
+}
+
+bool QCALLTYPE EventPipeInternal::GetNextEvent(EventPipeEventInstanceData *pInstance)
+{
+ QCALL_CONTRACT;
+
+ EventPipeEventInstance *pNextInstance = NULL;
+ BEGIN_QCALL;
+
+ _ASSERTE(pInstance != NULL);
+
+ pNextInstance = EventPipe::GetNextEvent();
+ if (pNextInstance)
+ {
+ pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider();
+ pInstance->EventID = pNextInstance->GetEvent()->GetEventID();
+ pInstance->ThreadID = pNextInstance->GetThreadId();
+ pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart;
+ pInstance->ActivityId = *pNextInstance->GetActivityId();
+ pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId();
+ pInstance->Payload = pNextInstance->GetData();
+ pInstance->PayloadLength = pNextInstance->GetDataLength();
+ }
+
+ END_QCALL;
+ return pNextInstance != NULL;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeinternal.h b/src/vm/eventpipeinternal.h
new file mode 100644
index 0000000000..bbd4ad633b
--- /dev/null
+++ b/src/vm/eventpipeinternal.h
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPEINTERNAL_H__
+#define __EVENTPIPEINTERNAL_H__
+
+#ifdef FEATURE_PERFTRACING
+
+// TODO: Maybe we should move the other types that are used on PInvoke here?
+
+class EventPipeInternal
+{
+private:
+ enum class ActivityControlCode
+ {
+ EVENT_ACTIVITY_CONTROL_GET_ID = 1,
+ EVENT_ACTIVITY_CONTROL_SET_ID = 2,
+ EVENT_ACTIVITY_CONTROL_CREATE_ID = 3,
+ EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4,
+ EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
+ };
+
+ struct EventPipeEventInstanceData
+ {
+ void *ProviderID;
+ unsigned int EventID;
+ unsigned int ThreadID;
+ LARGE_INTEGER TimeStamp;
+ GUID ActivityId;
+ GUID RelatedActivityId;
+ const BYTE *Payload;
+ unsigned int PayloadLength;
+ };
+
+ struct EventPipeSessionInfo
+ {
+ FILETIME StartTimeAsUTCFileTime;
+ LARGE_INTEGER StartTimeStamp;
+ LARGE_INTEGER TimeStampFrequency;
+ };
+
+public:
+ //!
+ //! Sets the sampling rate and enables the event pipe for the specified configuration.
+ //!
+ static UINT64 QCALLTYPE Enable(
+ __in_z LPCWSTR outputFile,
+ UINT32 circularBufferSizeInMB,
+ INT64 profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration *pProviders,
+ UINT32 numProviders,
+ UINT64 multiFileTraceLengthInSeconds);
+
+ //! TODO: Add a ListActiveSessions to get the live SessionID in order to Disable?
+
+ //!
+ //! Disables the specified session Id.
+ //!
+ static void QCALLTYPE Disable(UINT64 sessionID);
+
+ static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo);
+
+ static INT_PTR QCALLTYPE CreateProvider(
+ __in_z LPCWSTR providerName,
+ EventPipeCallback pCallbackFunc);
+
+ static INT_PTR QCALLTYPE DefineEvent(
+ INT_PTR provHandle,
+ UINT32 eventID,
+ __int64 keywords,
+ UINT32 eventVersion,
+ UINT32 level,
+ void *pMetadata,
+ UINT32 metadataLength);
+
+ static INT_PTR QCALLTYPE GetProvider(
+ __in_z LPCWSTR providerName);
+
+ static void QCALLTYPE DeleteProvider(
+ INT_PTR provHandle);
+
+ static int QCALLTYPE EventActivityIdControl(
+ uint32_t controlCode,
+ GUID *pActivityId);
+
+ static void QCALLTYPE WriteEvent(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ void *pData,
+ UINT32 length,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static void QCALLTYPE WriteEventData(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ EventData *pEventData,
+ UINT32 eventDataCount,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static bool QCALLTYPE GetNextEvent(
+ EventPipeEventInstanceData *pInstance);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPEINTERNAL_H__
diff --git a/src/vm/eventpipeprotocolhelper.cpp b/src/vm/eventpipeprotocolhelper.cpp
new file mode 100644
index 0000000000..bfcdcfa2f8
--- /dev/null
+++ b/src/vm/eventpipeprotocolhelper.cpp
@@ -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.
+
+#include "common.h"
+#include "eventpipeprotocolhelper.h"
+#include "diagnosticsipc.h"
+#include "diagnosticsprotocol.h"
+
+#ifdef FEATURE_PERFTRACING
+
+bool EventPipeProtocolHelper::TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
+{
+ // Picking an arbitrary upper bound,
+ // This should be larger than any reasonable client request.
+ const uint32_t MaxCountConfigs = 1000; // TODO: This might be too large.
+
+ uint32_t countConfigs = 0;
+ if (!TryParse(bufferCursor, bufferLen, countConfigs))
+ return false;
+ if (countConfigs > MaxCountConfigs)
+ return false;
+ EventPipeProviderConfiguration *pConfigs = result.AllocNoThrow(countConfigs);
+ if (pConfigs == nullptr)
+ return false;
+
+ for (uint32_t i = 0; i < countConfigs; i++)
+ {
+ uint64_t keywords = 0;
+ if (!TryParse(bufferCursor, bufferLen, keywords))
+ return false;
+
+ uint32_t logLevel = 0;
+ if (!TryParse(bufferCursor, bufferLen, logLevel))
+ return false;
+ if (logLevel > 5) // (logLevel > EventPipeEventLevel::Verbose)
+ return false;
+
+ LPCWSTR pProviderName = nullptr;
+ if (!TryParseString(bufferCursor, bufferLen, pProviderName))
+ return false;
+ if (wcslen(pProviderName) == 0)
+ return false; // TODO: Should we ignore these input?
+
+ LPCWSTR pFilterData = nullptr; // This parameter is optional.
+ TryParseString(bufferCursor, bufferLen, pFilterData);
+
+ pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData);
+ }
+ return true;
+}
+
+void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ // TODO: Read within a loop.
+ const uint32_t BufferSize = 8192;
+ uint8_t buffer[BufferSize]{};
+ uint32_t nNumberOfBytesRead = 0;
+ bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ // The protocol buffer is defined as:
+ // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z
+ // message = uint circularBufferMB, ulong multiFileTraceLength, string outputPath, array<provider_config> providers
+ // uint = 4 little endian bytes
+ // ulong = 8 little endian bytes
+ // wchar = 2 little endian bytes, UTF16 encoding
+ // array<T> = uint length, length # of Ts
+ // string = (array<char> where the last char must = 0) or (length = 0)
+ // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data
+
+ LPCWSTR strOutputPath;
+ uint32_t circularBufferSizeInMB = EventPipeProtocolHelper::DefaultCircularBufferMB;
+ uint64_t multiFileTraceLengthInSeconds = EventPipeProtocolHelper::DefaultMultiFileTraceLengthInSeconds;
+ CQuickArray<EventPipeProviderConfiguration> providerConfigs;
+
+ uint8_t *pBufferCursor = buffer;
+ uint32_t bufferLen = nNumberOfBytesRead;
+ if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
+ !TryParse(pBufferCursor, bufferLen, multiFileTraceLengthInSeconds) ||
+ !TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
+ !TryParseProviderConfigurations(pBufferCursor, bufferLen, providerConfigs))
+ {
+ return; // TODO: error handling
+ }
+
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ if (providerConfigs.Size() > 0)
+ {
+ sessionId = EventPipe::Enable(
+ strOutputPath, // outputFile
+ circularBufferSizeInMB, // circularBufferSizeInMB
+ DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
+ providerConfigs.Ptr(), // pConfigs
+ static_cast<uint32_t>(providerConfigs.Size()), // numConfigs
+ multiFileTraceLengthInSeconds); // multiFileTraceLengthInSeconds
+ }
+
+ uint32_t nBytesWritten = 0;
+ fSuccess = pStream->Write(&sessionId, sizeof(sessionId), nBytesWritten);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ fSuccess = pStream->Flush();
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+ delete pStream;
+}
+
+void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ uint32_t nNumberOfBytesRead = 0;
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ const bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead);
+ if (!fSuccess || nNumberOfBytesRead != sizeof(sessionId))
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ EventPipe::Disable(sessionId);
+ // TODO: Should we acknowledge back?
+ delete pStream;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeprotocolhelper.h b/src/vm/eventpipeprotocolhelper.h
new file mode 100644
index 0000000000..fb3f65a247
--- /dev/null
+++ b/src/vm/eventpipeprotocolhelper.h
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_PROTOCOL_HELPER_H__
+#define __EVENTPIPE_PROTOCOL_HELPER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "common.h"
+#include "eventpipe.h"
+
+class IpcStream;
+
+class EventPipeProtocolHelper
+{
+public:
+ // IPC event handlers.
+ static void EnableFileTracingEventHandler(IpcStream *pStream);
+ static void DisableTracingEventHandler(IpcStream *pStream);
+
+private:
+ const static uint32_t DefaultCircularBufferMB = 1024; // 1 GB
+ const static uint64_t DefaultMultiFileTraceLengthInSeconds = 0;
+ const static uint32_t DefaultProfilerSamplingRateInNanoseconds = 1000000;
+
+ //! Read a list of providers: "Provider[,Provider]"
+ //! Provider: "(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]"
+ //! KeyValueArgs: "[key1=value1][;key2=value2]"
+ static bool TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_PROTOCOL_HELPER_H__
diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h
index dffdc7e3db..cf89cf3229 100644
--- a/src/vm/eventpipeprovider.h
+++ b/src/vm/eventpipeprovider.h
@@ -72,7 +72,7 @@ public:
// Create a new event.
EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
- private:
+private:
// Create a new event, but allow needStack to be specified.
// In general, we want stack walking to be controlled by the consumer and not the producer of events.
diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp
index 7388472c7d..03bc6c681a 100644
--- a/src/vm/eventpipesession.cpp
+++ b/src/vm/eventpipesession.cpp
@@ -6,30 +6,30 @@
#include "eventpipe.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
#ifdef FEATURE_PERFTRACING
EventPipeSession::EventPipeSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- unsigned int numProviders,
- UINT64 multiFileTraceLengthInSeconds)
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
m_sessionType = sessionType;
m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB;
m_rundownEnabled = false;
- m_pProviderList = new EventPipeSessionProviderList(
- pProviders,
- numProviders);
+ m_pProviderList = new EventPipeSessionProviderList(pProviders, numProviders);
m_multiFileTraceLengthInSeconds = multiFileTraceLengthInSeconds;
GetSystemTimeAsFileTime(&m_sessionStartTime);
QueryPerformanceCounter(&m_sessionStartTimeStamp);
@@ -90,218 +90,4 @@ EventPipeSessionProvider* EventPipeSession::GetSessionProvider(EventPipeProvider
return m_pProviderList->GetSessionProvider(pProvider);
}
-EventPipeSessionProviderList::EventPipeSessionProviderList(
- EventPipeProviderConfiguration *pConfigs,
- unsigned int numConfigs)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- m_pProviders = new SList<SListElem<EventPipeSessionProvider*>>();
- m_pCatchAllProvider = NULL;
- for(unsigned int i=0; i<numConfigs; i++)
- {
- EventPipeProviderConfiguration *pConfig = &pConfigs[i];
-
- // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose.
- if((wcscmp(W("*"), pConfig->GetProviderName()) == 0) && (pConfig->GetKeywords() == 0xFFFFFFFFFFFFFFFF) && ((EventPipeEventLevel)pConfig->GetLevel() == EventPipeEventLevel::Verbose) && (m_pCatchAllProvider == NULL))
- {
- m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose, NULL);
- }
- else
- {
- EventPipeSessionProvider *pProvider = new EventPipeSessionProvider(
- pConfig->GetProviderName(),
- pConfig->GetKeywords(),
- (EventPipeEventLevel)pConfig->GetLevel(),
- pConfig->GetFilterData());
-
- m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider));
- }
- }
-}
-
-EventPipeSessionProviderList::~EventPipeSessionProviderList()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(m_pProviders != NULL)
- {
- SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead();
- while(pElem != NULL)
- {
- EventPipeSessionProvider *pProvider = pElem->GetValue();
- delete pProvider;
-
- SListElem<EventPipeSessionProvider*> *pCurElem = pElem;
- pElem = m_pProviders->GetNext(pElem);
- delete pCurElem;
- }
-
- delete m_pProviders;
- m_pProviders = NULL;
- }
- if(m_pCatchAllProvider != NULL)
- {
- delete(m_pCatchAllProvider);
- m_pCatchAllProvider = NULL;
- }
-}
-
-void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(pProvider != NULL)
- {
- m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider));
- }
-}
-
-EventPipeSessionProvider* EventPipeSessionProviderList::GetSessionProvider(
- EventPipeProvider *pProvider)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config.
- if(m_pCatchAllProvider != NULL)
- {
- return m_pCatchAllProvider;
- }
-
- if(m_pProviders == NULL)
- {
- return NULL;
- }
-
- SString providerNameStr = pProvider->GetProviderName();
- LPCWSTR providerName = providerNameStr.GetUnicode();
-
- EventPipeSessionProvider *pSessionProvider = NULL;
- SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead();
- while(pElem != NULL)
- {
- EventPipeSessionProvider *pCandidate = pElem->GetValue();
- if(wcscmp(providerName, pCandidate->GetProviderName()) == 0)
- {
- pSessionProvider = pCandidate;
- break;
- }
- pElem = m_pProviders->GetNext(pElem);
- }
-
- return pSessionProvider;
-}
-
-bool EventPipeSessionProviderList::IsEmpty() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL);
-}
-
-EventPipeSessionProvider::EventPipeSessionProvider(
- LPCWSTR providerName,
- UINT64 keywords,
- EventPipeEventLevel loggingLevel,
- LPCWSTR filterData)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(providerName != NULL)
- {
- size_t bufSize = wcslen(providerName) + 1;
- m_pProviderName = new WCHAR[bufSize];
- wcscpy_s(m_pProviderName, bufSize, providerName);
- }
- else
- {
- m_pProviderName = NULL;
- }
- m_keywords = keywords;
- m_loggingLevel = loggingLevel;
-
- if(filterData != NULL)
- {
- size_t bufSize = wcslen(filterData) + 1;
- m_pFilterData = new WCHAR[bufSize];
- wcscpy_s(m_pFilterData, bufSize, filterData);
- }
- else
- {
- m_pFilterData = NULL;
- }
-}
-
-EventPipeSessionProvider::~EventPipeSessionProvider()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // C++ standard, $5.3.5/2: Deleting a NULL pointer is safe.
- delete[] m_pProviderName;
- m_pProviderName = NULL;
-
- delete[] m_pFilterData;
- m_pFilterData = NULL;
-}
-
-LPCWSTR EventPipeSessionProvider::GetProviderName() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_pProviderName;
-}
-
-UINT64 EventPipeSessionProvider::GetKeywords() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_keywords;
-}
-
-EventPipeEventLevel EventPipeSessionProvider::GetLevel() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_loggingLevel;
-}
-
-LPCWSTR EventPipeSessionProvider::GetFilterData() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_pFilterData;
-}
-
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h
index 3c4f293407..0474c7cafe 100644
--- a/src/vm/eventpipesession.h
+++ b/src/vm/eventpipesession.h
@@ -7,15 +7,14 @@
#ifdef FEATURE_PERFTRACING
-enum class EventPipeEventLevel;
-struct EventPipeProviderConfiguration;
class EventPipeSessionProviderList;
class EventPipeSessionProvider;
enum class EventPipeSessionType
{
File,
- Streaming
+ Streaming,
+ IpcStream
};
class EventPipeSession
@@ -49,10 +48,9 @@ public:
EventPipeSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- unsigned int numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds);
~EventPipeSession();
// Determine if the session is valid or not. Invalid sessions can be detected before they are enabled.
@@ -113,68 +111,6 @@ public:
EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
};
-class EventPipeSessionProviderList
-{
-
-private:
-
- // The list of providers.
- SList<SListElem<EventPipeSessionProvider*>> *m_pProviders;
-
- // A catch-all provider used when tracing is enabled for all events.
- EventPipeSessionProvider *m_pCatchAllProvider;
-
-public:
-
- // Create a new list based on the input.
- EventPipeSessionProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int numConfigs);
- ~EventPipeSessionProviderList();
-
- // Add a new session provider to the list.
- void AddSessionProvider(EventPipeSessionProvider *pProvider);
-
- // Get the session provider for the specified provider.
- // Return NULL if one doesn't exist.
- EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
-
- // Returns true if the list is empty.
- bool IsEmpty() const;
-};
-
-class EventPipeSessionProvider
-{
-private:
-
- // The provider name.
- WCHAR *m_pProviderName;
-
- // The enabled keywords.
- UINT64 m_keywords;
-
- // The loging level.
- EventPipeEventLevel m_loggingLevel;
-
- // The filter data.
- WCHAR *m_pFilterData;
-
-public:
-
- EventPipeSessionProvider(
- LPCWSTR providerName,
- UINT64 keywords,
- EventPipeEventLevel loggingLevel,
- LPCWSTR filterData);
- ~EventPipeSessionProvider();
-
- LPCWSTR GetProviderName() const;
-
- UINT64 GetKeywords() const;
-
- EventPipeEventLevel GetLevel() const;
-
- LPCWSTR GetFilterData() const;
-};
-
#endif // FEATURE_PERFTRACING
#endif // __EVENTPIPE_SESSION_H__
diff --git a/src/vm/eventpipesessionprovider.cpp b/src/vm/eventpipesessionprovider.cpp
new file mode 100644
index 0000000000..7e79c47958
--- /dev/null
+++ b/src/vm/eventpipesessionprovider.cpp
@@ -0,0 +1,195 @@
+// 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.
+
+#include "common.h"
+#include "eventpipeprovider.h"
+#include "eventpipesessionprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeSessionProvider::EventPipeSessionProvider(
+ LPCWSTR providerName,
+ UINT64 keywords,
+ EventPipeEventLevel loggingLevel,
+ LPCWSTR filterData)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (providerName != NULL)
+ {
+ size_t bufSize = wcslen(providerName) + 1;
+ m_pProviderName = new WCHAR[bufSize];
+ wcscpy_s(m_pProviderName, bufSize, providerName);
+ }
+ else
+ {
+ m_pProviderName = NULL;
+ }
+
+ m_keywords = keywords;
+ m_loggingLevel = loggingLevel;
+
+ if (filterData != NULL)
+ {
+ size_t bufSize = wcslen(filterData) + 1;
+ m_pFilterData = new WCHAR[bufSize];
+ wcscpy_s(m_pFilterData, bufSize, filterData);
+ }
+ else
+ {
+ m_pFilterData = NULL;
+ }
+}
+
+EventPipeSessionProvider::~EventPipeSessionProvider()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ delete[] m_pProviderName;
+ delete[] m_pFilterData;
+}
+
+EventPipeSessionProviderList::EventPipeSessionProviderList(
+ const EventPipeProviderConfiguration *pConfigs,
+ uint32_t numConfigs)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION((numConfigs == 0) || (numConfigs > 0 && pConfigs != nullptr));
+ }
+ CONTRACTL_END;
+
+ m_pProviders = new SList<SListElem<EventPipeSessionProvider *>>();
+ m_pCatchAllProvider = NULL;
+
+ if ((numConfigs > 0) && (pConfigs == nullptr))
+ return; // TODO: This seems the logical thing to do here.
+
+ for (uint32_t i = 0; i < numConfigs; ++i)
+ {
+ const EventPipeProviderConfiguration *pConfig = &pConfigs[i];
+
+ // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose.
+ if ((wcscmp(W("*"), pConfig->GetProviderName()) == 0) && (pConfig->GetKeywords() == 0xFFFFFFFFFFFFFFFF) && ((EventPipeEventLevel)pConfig->GetLevel() == EventPipeEventLevel::Verbose) && (m_pCatchAllProvider == NULL))
+ {
+ m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose, NULL);
+ }
+ else
+ {
+ EventPipeSessionProvider *pProvider = new EventPipeSessionProvider(
+ pConfig->GetProviderName(),
+ pConfig->GetKeywords(),
+ (EventPipeEventLevel)pConfig->GetLevel(),
+ pConfig->GetFilterData());
+
+ m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider *>(pProvider));
+ }
+ }
+}
+
+EventPipeSessionProviderList::~EventPipeSessionProviderList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pProviders != NULL)
+ {
+ SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead();
+ while (pElem != NULL)
+ {
+ EventPipeSessionProvider *pProvider = pElem->GetValue();
+ delete pProvider;
+
+ SListElem<EventPipeSessionProvider *> *pCurElem = pElem;
+ pElem = m_pProviders->GetNext(pElem);
+ delete pCurElem;
+ }
+
+ delete m_pProviders;
+ }
+
+ delete m_pCatchAllProvider;
+}
+
+void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pProvider != nullptr);
+ }
+ CONTRACTL_END;
+
+ if (pProvider != nullptr)
+ m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider *>(pProvider));
+}
+
+EventPipeSessionProvider *EventPipeSessionProviderList::GetSessionProvider(
+ EventPipeProvider *pProvider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pProvider != nullptr); // TODO: This seems like a reasonable pre-condition
+ }
+ CONTRACTL_END;
+
+ // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config.
+ if (m_pCatchAllProvider != NULL)
+ return m_pCatchAllProvider;
+
+ if (m_pProviders == NULL)
+ return NULL;
+
+ SString providerNameStr = pProvider->GetProviderName();
+ LPCWSTR providerName = providerNameStr.GetUnicode();
+
+ EventPipeSessionProvider *pSessionProvider = NULL;
+ SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead();
+ while (pElem != NULL)
+ {
+ EventPipeSessionProvider *pCandidate = pElem->GetValue();
+ if (wcscmp(providerName, pCandidate->GetProviderName()) == 0)
+ {
+ pSessionProvider = pCandidate;
+ break;
+ }
+ pElem = m_pProviders->GetNext(pElem);
+ }
+
+ return pSessionProvider;
+}
+
+bool EventPipeSessionProviderList::IsEmpty() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL);
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipesessionprovider.h b/src/vm/eventpipesessionprovider.h
new file mode 100644
index 0000000000..7b500e4412
--- /dev/null
+++ b/src/vm/eventpipesessionprovider.h
@@ -0,0 +1,81 @@
+#ifndef __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
+#define __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
+
+#ifdef FEATURE_PERFTRACING
+
+enum class EventPipeEventLevel;
+class EventPipeProvider;
+
+class EventPipeSessionProvider
+{
+public:
+ EventPipeSessionProvider(
+ LPCWSTR providerName,
+ UINT64 keywords,
+ EventPipeEventLevel loggingLevel,
+ LPCWSTR filterData);
+ ~EventPipeSessionProvider();
+
+ LPCWSTR GetProviderName() const
+ {
+ return m_pProviderName;
+ }
+
+ UINT64 GetKeywords() const
+ {
+ return m_keywords;
+ }
+
+ EventPipeEventLevel GetLevel() const
+ {
+ return m_loggingLevel;
+ }
+
+ LPCWSTR GetFilterData() const
+ {
+ return m_pFilterData;
+ }
+
+private:
+ WCHAR *m_pProviderName;
+ UINT64 m_keywords;
+ EventPipeEventLevel m_loggingLevel;
+ WCHAR *m_pFilterData;
+};
+
+class EventPipeSessionProviderList
+{
+public:
+
+ // Create a new list based on the input.
+ EventPipeSessionProviderList(
+ const EventPipeProviderConfiguration *pConfigs,
+ uint32_t numConfigs);
+ ~EventPipeSessionProviderList();
+
+ // Add a new session provider to the list.
+ void AddSessionProvider(EventPipeSessionProvider *pProvider);
+
+ // Get the session provider for the specified provider.
+ // Return NULL if one doesn't exist.
+ EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
+
+ // Returns true if the list is empty.
+ bool IsEmpty() const;
+
+ EventPipeSessionProviderList() = delete;
+ EventPipeSessionProviderList(const EventPipeSessionProviderList &other) = delete;
+ EventPipeSessionProviderList(EventPipeSessionProviderList &&other) = delete;
+ EventPipeSessionProviderList &operator=(const EventPipeSessionProviderList &rhs) = delete;
+ EventPipeSessionProviderList &&operator=(EventPipeSessionProviderList &&rhs) = delete;
+
+private:
+ SList<SListElem<EventPipeSessionProvider*>> *m_pProviders = nullptr;
+
+ // A catch-all provider used when tracing is enabled for all events.
+ EventPipeSessionProvider *m_pCatchAllProvider = nullptr;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
diff --git a/src/vm/excep.cpp b/src/vm/excep.cpp
index 26003f0597..6d62f9c691 100644
--- a/src/vm/excep.cpp
+++ b/src/vm/excep.cpp
@@ -2983,6 +2983,7 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL r
// User hits 'g'
// Then debugger can bring us here.
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ UNREACHABLE();
}
@@ -11945,6 +11946,7 @@ void ExceptionNotifications::GetEventArgsForNotification(ExceptionNotificationHa
static LONG ExceptionNotificationFilter(PEXCEPTION_POINTERS pExceptionInfo, LPVOID pParam)
{
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ return -1;
}
#ifdef FEATURE_CORRUPTING_EXCEPTIONS
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index 838450bb0b..8574caf6f1 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -4693,6 +4693,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
_ASSERTE(!"UnwindManagedExceptionPass1: Failed to find a handler. Reached the end of the stack");
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ UNREACHABLE();
}
VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException)
diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp
index 7a79b2ff19..89dbe50728 100644
--- a/src/vm/fastserializer.cpp
+++ b/src/vm/fastserializer.cpp
@@ -11,7 +11,47 @@
// As a result of work on V3 of Event Pipe (https://github.com/Microsoft/perfview/pull/532) it got removed
// if you need it, please use git to restore it
-FastSerializer::FastSerializer(SString &outputFilePath)
+IpcStreamWriter::IpcStreamWriter(IpcStream *pStream) : _pStream(pStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(_pStream != nullptr);
+ }
+ CONTRACTL_END;
+}
+
+IpcStreamWriter::~IpcStreamWriter()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ delete _pStream;
+}
+
+bool IpcStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(lpBuffer != nullptr);
+ PRECONDITION(nBytesToWrite > 0);
+ }
+ CONTRACTL_END;
+
+ return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten);
+}
+
+FileStreamWriter::FileStreamWriter(const SString &outputFilePath)
{
CONTRACTL
{
@@ -21,21 +61,17 @@ FastSerializer::FastSerializer(SString &outputFilePath)
}
CONTRACTL_END;
- m_writeErrorEncountered = false;
- m_currentPos = 0;
m_pFileStream = new CFileStream();
- if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
+ if (FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
{
_ASSERTE(!"Unable to open file for write.");
- delete(m_pFileStream);
+ delete m_pFileStream;
m_pFileStream = NULL;
return;
}
-
- WriteFileHeader();
}
-FastSerializer::~FastSerializer()
+FileStreamWriter::~FileStreamWriter()
{
CONTRACTL
{
@@ -45,18 +81,54 @@ FastSerializer::~FastSerializer()
}
CONTRACTL_END;
- if(m_pFileStream != NULL)
+ delete m_pFileStream;
+}
+
+bool FileStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ CONTRACTL
{
- delete(m_pFileStream);
- m_pFileStream = NULL;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(lpBuffer != nullptr);
+ PRECONDITION(nBytesToWrite > 0);
+ }
+ CONTRACTL_END;
+
+ ULONG outCount;
+ HRESULT hResult = m_pFileStream->Write(lpBuffer, nBytesToWrite, &outCount);
+ nBytesWritten = static_cast<uint32_t>(outCount);
+ return hResult == S_OK;
+}
+
+FastSerializer::FastSerializer(StreamWriter *pStreamWriter) : m_pStreamWriter(pStreamWriter)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_pStreamWriter != NULL);
}
+ CONTRACTL_END;
+
+ m_writeErrorEncountered = false;
+ m_currentPos = 0;
+ WriteFileHeader();
}
-StreamLabel FastSerializer::GetStreamLabel() const
+FastSerializer::~FastSerializer()
{
- LIMITED_METHOD_CONTRACT;
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
- return (StreamLabel)m_currentPos;
+ delete m_pStreamWriter;
}
void FastSerializer::WriteObject(FastSerializableObject *pObject)
@@ -92,15 +164,13 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
}
CONTRACTL_END;
- if(m_writeErrorEncountered || m_pFileStream == NULL)
- {
+ if (m_writeErrorEncountered || m_pStreamWriter == NULL)
return;
- }
EX_TRY
{
- ULONG outCount;
- m_pFileStream->Write(pBuffer, length, &outCount);
+ uint32_t outCount;
+ m_pStreamWriter->Write(pBuffer, length, outCount);
#ifdef _DEBUG
size_t prevPos = m_currentPos;
@@ -120,7 +190,7 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
EX_CATCH
{
m_writeErrorEncountered = true;
- }
+ }
EX_END_CATCH(SwallowAllExceptions);
}
@@ -142,10 +212,10 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
WriteTag(FastSerializerTags::NullReference);
// Write the SerializationType version fields.
- int serializationType[2];
- serializationType[0] = pObject->GetObjectVersion();
- serializationType[1] = pObject->GetMinReaderVersion();
- WriteBuffer((BYTE*) &serializationType, sizeof(serializationType));
+ int serializationType[2] = {
+ pObject->GetObjectVersion(),
+ pObject->GetMinReaderVersion()};
+ WriteBuffer((BYTE *)&serializationType, sizeof(serializationType));
// Write the SerializationType TypeName field.
const char *strTypeName = pObject->GetTypeName();
@@ -157,7 +227,6 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
WriteTag(FastSerializerTags::EndObject);
}
-
void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength)
{
CONTRACTL
@@ -169,7 +238,7 @@ void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned in
CONTRACTL_END;
WriteBuffer((BYTE *)&tag, sizeof(tag));
- if(payload != NULL)
+ if (payload != NULL)
{
_ASSERTE(payloadLength > 0);
WriteBuffer(payload, payloadLength);
@@ -202,10 +271,10 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length)
CONTRACTL_END;
// Write the string length .
- WriteBuffer((BYTE*) &length, sizeof(length));
+ WriteBuffer((BYTE *)&length, sizeof(length));
// Write the string contents.
- WriteBuffer((BYTE*) strContents, length);
+ WriteBuffer((BYTE *)strContents, length);
}
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/fastserializer.h b/src/vm/fastserializer.h
index fad04de48c..3b4de65bf5 100644
--- a/src/vm/fastserializer.h
+++ b/src/vm/fastserializer.h
@@ -11,23 +11,20 @@
#include "fastserializableobject.h"
#include "fstream.h"
-
-class FastSerializer;
-
-typedef unsigned int StreamLabel;
+#include "diagnosticsipc.h"
// the enumeration has a specific set of values to keep it compatible with consumer library
// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295
-enum class FastSerializerTags : BYTE
+enum class FastSerializerTags : BYTE
{
- Error = 0, // To improve debugabilty, 0 is an illegal tag.
- NullReference = 1, // Tag for a null object forwardReference.
- ObjectReference = 2, // Followed by StreamLabel
- // 3 used to belong to ForwardReference, which got removed in V3
+ Error = 0, // To improve debugabilty, 0 is an illegal tag.
+ NullReference = 1, // Tag for a null object forwardReference.
+ ObjectReference = 2, // Followed by StreamLabel
+ // 3 used to belong to ForwardReference, which got removed in V3
BeginObject = 4, // Followed by Type object, object data, tagged EndObject
- BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation
- EndObject = 6, // Placed after an object to mark its end.
- // 7 used to belong to ForwardDefinition, which got removed in V3
+ BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation
+ EndObject = 6, // Placed after an object to mark its end.
+ // 7 used to belong to ForwardDefinition, which got removed in V3
Byte = 8,
Int16,
Int32,
@@ -35,17 +32,53 @@ enum class FastSerializerTags : BYTE
SkipRegion,
String,
Blob,
- Limit // Just past the last valid tag, used for asserts.
+ Limit // Just past the last valid tag, used for asserts.
};
-class FastSerializer
+//!
+//! Provides a generic interface for writing a sequence of bytes to a stream.
+//!
+class StreamWriter
{
public:
+ StreamWriter() = default;
+ virtual ~StreamWriter() = default;
+ virtual bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const = 0;
+};
- FastSerializer(SString &outputFilePath);
- ~FastSerializer();
+//!
+//! Implements a StreamWriter for writing bytes to an IPC.
+//!
+class IpcStreamWriter final : public StreamWriter
+{
+public:
+ IpcStreamWriter(IpcStream *pStream);
+ ~IpcStreamWriter();
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
+
+private:
+ IpcStream *const _pStream;
+};
+
+//!
+//! Implements a StreamWriter for writing bytes to an File.
+//!
+class FileStreamWriter final : public StreamWriter
+{
+public:
+ FileStreamWriter(const SString &outputFilePath);
+ ~FileStreamWriter();
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
+
+private:
+ CFileStream *m_pFileStream;
+};
- StreamLabel GetStreamLabel() const;
+class FastSerializer
+{
+public:
+ FastSerializer(StreamWriter *pStreamWriter);
+ ~FastSerializer();
void WriteObject(FastSerializableObject *pObject);
void WriteBuffer(BYTE *pBuffer, unsigned int length);
@@ -55,16 +88,14 @@ public:
size_t GetCurrentPosition() const
{
LIMITED_METHOD_CONTRACT;
-
return m_currentPos;
}
private:
-
void WriteSerializationType(FastSerializableObject *pObject);
void WriteFileHeader();
- CFileStream *m_pFileStream;
+ StreamWriter *const m_pStreamWriter;
bool m_writeErrorEncountered;
size_t m_currentPos;
};
diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp
index 0e37931fe9..4d6a19c98d 100644
--- a/src/vm/gcenv.os.cpp
+++ b/src/vm/gcenv.os.cpp
@@ -494,16 +494,21 @@ static size_t GetRestrictedPhysicalMemoryLimit()
if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
- job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
- job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
+ if ((job_memory_limit != (size_t)MAX_PTR) ||
+ (job_process_memory_limit != (size_t)MAX_PTR) ||
+ (job_workingset_limit != (size_t)MAX_PTR))
+ {
+ job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
+ job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
- MEMORYSTATUSEX ms;
- ::GetProcessMemoryLoad(&ms);
- total_virtual = ms.ullTotalVirtual;
- total_physical = ms.ullAvailPhys;
+ MEMORYSTATUSEX ms;
+ ::GetProcessMemoryLoad(&ms);
+ total_virtual = ms.ullTotalVirtual;
+ total_physical = ms.ullAvailPhys;
- // A sanity check in case someone set a larger limit than there is actual physical memory.
- job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ // A sanity check in case someone set a larger limit than there is actual physical memory.
+ job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ }
}
}
diff --git a/src/vm/gchandleutilities.cpp b/src/vm/gchandleutilities.cpp
index 220445a1cc..a4fdec3c3f 100644
--- a/src/vm/gchandleutilities.cpp
+++ b/src/vm/gchandleutilities.cpp
@@ -33,7 +33,7 @@ void ValidateHandleAssignment(OBJECTHANDLE handle, OBJECTREF objRef)
#endif
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
- ADIndex appDomainIndex = ADIndex(reinterpret_cast<DWORD>(mgr->GetHandleContext(handle)));
+ ADIndex appDomainIndex = ADIndex((DWORD)((SIZE_T)mgr->GetHandleContext(handle)));
ValidateObjectAndAppDomain(objRef, appDomainIndex);
#endif // _DEBUG_IMPL
diff --git a/src/vm/gchandleutilities.h b/src/vm/gchandleutilities.h
index 86227cdd6a..8c26737284 100644
--- a/src/vm/gchandleutilities.h
+++ b/src/vm/gchandleutilities.h
@@ -42,7 +42,7 @@ inline OBJECTREF ObjectFromHandle(OBJECTHANDLE handle)
#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
// not allowed to dispatch virtually on a IGCHandleManager when compiling for DAC
- DWORD context = (DWORD)GCHandleUtilities::GetGCHandleManager()->GetHandleContext(handle);
+ DWORD context = (DWORD)(SIZE_T)GCHandleUtilities::GetGCHandleManager()->GetHandleContext(handle);
OBJECTREF objRef = ObjectToOBJECTREF(*(Object**)handle);
ValidateObjectAndAppDomain(objRef, ADIndex(context));
diff --git a/src/vm/gchelpers.cpp b/src/vm/gchelpers.cpp
index a52e10bb4f..af3a1602b3 100644
--- a/src/vm/gchelpers.cpp
+++ b/src/vm/gchelpers.cpp
@@ -981,6 +981,8 @@ STRINGREF SlowAllocateString( DWORD cchStringLength )
// Limit the maximum string size to <2GB to mitigate risk of security issues caused by 32-bit integer
// overflows in buffer size calculations.
+ //
+ // If the value below is changed, also change SlowAllocateUtf8String.
if (cchStringLength > 0x3FFFFFDF)
ThrowOutOfMemory();
@@ -1028,6 +1030,81 @@ STRINGREF SlowAllocateString( DWORD cchStringLength )
return( ObjectToSTRINGREF(orObject) );
}
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String(DWORD cchStringLength)
+{
+ CONTRACTL{
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
+ } CONTRACTL_END;
+
+ Utf8StringObject *orObject = NULL;
+
+#ifdef _DEBUG
+ if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
+ {
+ char *a = new char;
+ delete a;
+ }
+#endif
+
+ // Limit the maximum string size to <2GB to mitigate risk of security issues caused by 32-bit integer
+ // overflows in buffer size calculations.
+ //
+ // 0x7FFFFFBF is derived from the const 0x3FFFFFDF in SlowAllocateString.
+ // Adding +1 (for null terminator) and multiplying by sizeof(WCHAR) means that
+ // SlowAllocateString allows a maximum of 0x7FFFFFC0 bytes to be used for the
+ // string data itself, with some additional buffer for object headers and other
+ // data. Since we don't have the sizeof(WCHAR) multiplication here, we only need
+ // -1 to account for the null terminator, leading to a max size of 0x7FFFFFBF.
+ if (cchStringLength > 0x7FFFFFBF)
+ ThrowOutOfMemory();
+
+ SIZE_T ObjectSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength));
+ _ASSERTE(ObjectSize > cchStringLength);
+
+ SetTypeHandleOnThreadForAlloc(TypeHandle(g_pUtf8StringClass));
+
+ orObject = (Utf8StringObject *)Alloc(ObjectSize, FALSE, FALSE);
+
+ // Object is zero-init already
+ _ASSERTE(orObject->HasEmptySyncBlockInfo());
+
+ // Initialize Object
+ orObject->SetMethodTable(g_pUtf8StringClass);
+ orObject->SetLength(cchStringLength);
+
+ if (ObjectSize >= LARGE_OBJECT_SIZE)
+ {
+ GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
+ }
+
+ // Notify the profiler of the allocation
+ if (TrackAllocations())
+ {
+ OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
+ GCPROTECT_BEGIN(objref);
+ ProfilerObjectAllocatedCallback(objref, (ClassID)orObject->GetTypeHandle().AsPtr());
+ GCPROTECT_END();
+
+ orObject = (Utf8StringObject *)OBJECTREFToObject(objref);
+ }
+
+#ifdef FEATURE_EVENT_TRACE
+ // Send ETW event for allocation
+ if (ETW::TypeSystemLog::IsHeapAllocEventEnabled())
+ {
+ ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
+ }
+#endif // FEATURE_EVENT_TRACE
+
+ LogAlloc(ObjectSize, g_pUtf8StringClass, orObject);
+
+ return( ObjectToUTF8STRINGREF(orObject) );
+}
+#endif // FEATURE_UTF8STRING
+
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
// OBJECTREF AllocateComClassObject(ComClassFactory* pComClsFac)
void AllocateComClassObject(ComClassFactory* pComClsFac, OBJECTREF* ppRefClass)
diff --git a/src/vm/gchelpers.h b/src/vm/gchelpers.h
index 0e407c6e61..8f6a16ade9 100644
--- a/src/vm/gchelpers.h
+++ b/src/vm/gchelpers.h
@@ -71,6 +71,10 @@ STRINGREF AllocateString( DWORD cchStringLength );
// The slow version, implemented in gcscan.cpp
STRINGREF SlowAllocateString( DWORD cchStringLength );
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength );
+#endif // FEATURE_UTF8STRING
+
#else
// On other platforms, go to the (somewhat less efficient) implementations in gcscan.cpp
@@ -83,6 +87,10 @@ OBJECTREF AllocateObjectArray(DWORD cElements, TypeHandle ElementType, BOOL bAll
STRINGREF SlowAllocateString( DWORD cchStringLength );
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength );
+#endif // FEATURE_UTF8STRING
+
inline STRINGREF AllocateString( DWORD cchStringLength )
{
WRAPPER_NO_CONTRACT;
@@ -92,6 +100,15 @@ inline STRINGREF AllocateString( DWORD cchStringLength )
#endif
+#ifdef FEATURE_UTF8STRING
+inline UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SlowAllocateUtf8String(cchStringLength);
+}
+#endif // FEATURE_UTF8STRING
+
OBJECTREF DupArrayForCloning(BASEARRAYREF pRef, BOOL bAllocateInLargeHeap = FALSE);
// The JIT requests the EE to specify an allocation helper to use at each new-site.
diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp
index 4ab5336f49..d7803f0b3e 100644
--- a/src/vm/gdbjit.cpp
+++ b/src/vm/gdbjit.cpp
@@ -15,7 +15,11 @@
#include "gdbjit.h"
#include "gdbjithelpers.h"
+#ifndef __GNUC__
__declspec(thread) bool tls_isSymReaderInProgress = false;
+#else // !__GNUC__
+thread_local bool tls_isSymReaderInProgress = false;
+#endif // !__GNUC__
#ifdef _DEBUG
static void DumpElf(const char* methodName, const char *addr, size_t size)
diff --git a/src/vm/interpreter.cpp b/src/vm/interpreter.cpp
index 2f27b62e39..e0ad0937ac 100644
--- a/src/vm/interpreter.cpp
+++ b/src/vm/interpreter.cpp
@@ -1658,7 +1658,7 @@ void Interpreter::JitMethodIfAppropriate(InterpreterMethodInfo* interpMethInfo,
&status);
}
// This used to be a synchronous jit and could be made so again if desired,
- // but using ASP.Net MusicStore as an example scenario the performance is
+ // but using ASP .NET MusicStore as an example scenario the performance is
// better doing the JIT asynchronously. Given the not-on-by-default nature of the
// interpreter I didn't wring my hands too much trying to determine the ideal
// policy.
diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp
index 303f06130f..0576ca7336 100644
--- a/src/vm/jithelpers.cpp
+++ b/src/vm/jithelpers.cpp
@@ -2895,6 +2895,61 @@ HCIMPL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength)
}
HCIMPLEND
+#ifdef FEATURE_UTF8STRING
+HCIMPL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength)
+{
+ FCALL_CONTRACT;
+
+ do
+ {
+ _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
+
+ // Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
+ // problems, as well as making sure big string objects are correctly allocated in the big object heap.
+ if (stringLength >= LARGE_OBJECT_SIZE - 256)
+ {
+ break;
+ }
+
+ // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
+ // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
+ // some reshuffling of intermediate values into nonvolatile registers around the call.
+ Thread *thread = GetThread();
+
+ SIZE_T totalSize = Utf8StringObject::GetSize(stringLength);
+
+ // The method table's base size includes space for a terminating null character
+ _ASSERTE(totalSize >= g_pUtf8StringClass->GetBaseSize());
+ _ASSERTE(totalSize - g_pUtf8StringClass->GetBaseSize() == stringLength);
+
+ SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
+ _ASSERTE(alignedTotalSize >= totalSize);
+ totalSize = alignedTotalSize;
+
+ gc_alloc_context *allocContext = thread->GetAllocContext();
+ BYTE *allocPtr = allocContext->alloc_ptr;
+ _ASSERTE(allocPtr <= allocContext->alloc_limit);
+ if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
+ {
+ break;
+ }
+ allocContext->alloc_ptr = allocPtr + totalSize;
+
+ _ASSERTE(allocPtr != nullptr);
+ Utf8StringObject *stringObject = reinterpret_cast<Utf8StringObject *>(allocPtr);
+ stringObject->SetMethodTable(g_pUtf8StringClass);
+ stringObject->SetLength(stringLength);
+
+ return stringObject;
+ } while (false);
+
+ // Tail call to the slow helper
+ ENDFORBIDGC();
+ return HCCALL1(FramedAllocateUtf8String, stringLength);
+}
+HCIMPLEND
+#endif // FEATURE_UTF8STRING
+
#include <optdefault.h>
/*********************************************************************/
@@ -2933,6 +2988,22 @@ HCIMPL1(StringObject*, FramedAllocateString, DWORD stringLength)
}
HCIMPLEND
+#ifdef FEATURE_UTF8STRING
+HCIMPL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength)
+{
+ FCALL_CONTRACT;
+
+ UTF8STRINGREF result = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
+
+ result = SlowAllocateUtf8String(stringLength);
+
+ HELPER_METHOD_FRAME_END();
+ return((Utf8StringObject*) OBJECTREFToObject(result));
+}
+HCIMPLEND
+#endif // FEATURE_UTF8STRING
+
/*********************************************************************/
OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok)
{
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 6e4b288d66..b3ede3baa2 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -7493,6 +7493,56 @@ bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn,
return true;
}
+ if (tk == MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_BITWISE_EQUATABLE)->GetMemberDef())
+ {
+ _ASSERTE(ftn->HasMethodInstantiation());
+ Instantiation inst = ftn->GetMethodInstantiation();
+
+ _ASSERTE(ftn->GetNumGenericMethodArgs() == 1);
+ TypeHandle typeHandle = inst[0];
+ MethodTable * methodTable = typeHandle.GetMethodTable();
+
+ static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET };
+ static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET };
+
+ // Ideally we could detect automatically whether a type is trivially equatable
+ // (i.e., its operator == could be implemented via memcmp). But for now we'll
+ // do the simple thing and hardcode the list of types we know fulfill this contract.
+ // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented,
+ // as a method like CompareTo may need to take a type's signedness into account.
+
+ if (methodTable == MscorlibBinder::GetClass(CLASS__BOOLEAN)
+ || methodTable == MscorlibBinder::GetClass(CLASS__BYTE)
+ || methodTable == MscorlibBinder::GetClass(CLASS__SBYTE)
+#ifdef FEATURE_UTF8STRING
+ || methodTable == MscorlibBinder::GetClass(CLASS__CHAR8)
+#endif // FEATURE_UTF8STRING
+ || methodTable == MscorlibBinder::GetClass(CLASS__CHAR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT16)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT16)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT32)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT32)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT64)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT64)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INTPTR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINTPTR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__RUNE)
+ || methodTable->IsEnum())
+ {
+ methInfo->ILCode = const_cast<BYTE*>(returnTrue);
+ }
+ else
+ {
+ methInfo->ILCode = const_cast<BYTE*>(returnFalse);
+ }
+
+ methInfo->ILCodeSize = sizeof(returnTrue);
+ methInfo->maxStack = 1;
+ methInfo->EHcount = 0;
+ methInfo->options = (CorInfoOptions)0;
+ return true;
+ }
+
return false;
}
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index fe7dd4a922..af42bd29ab 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -231,6 +231,11 @@ extern FCDECL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength
extern FCDECL1(StringObject*, UnframedAllocateString, DWORD stringLength);
extern FCDECL1(StringObject*, FramedAllocateString, DWORD stringLength);
+#ifdef FEATURE_UTF8STRING
+extern FCDECL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength);
+extern FCDECL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength);
+#endif // FEATURE_UTF8STRING
+
extern FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1_R2R, CORINFO_CLASS_HANDLE arrayTypeHnd_, INT_PTR size);
diff --git a/src/vm/jitinterfacegen.cpp b/src/vm/jitinterfacegen.cpp
index f86011d3ef..3a5b618c26 100644
--- a/src/vm/jitinterfacegen.cpp
+++ b/src/vm/jitinterfacegen.cpp
@@ -80,6 +80,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
#else // FEATURE_PAL
// if (multi-proc || server GC)
if (GCHeapUtilities::UseThreadAllocationContexts())
@@ -91,6 +94,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_InlineGetThread);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastMP_InlineGetThread), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
}
else
{
@@ -105,6 +111,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_UP);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastUP), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
}
#endif // FEATURE_PAL
}
diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp
index 334a4a88e4..23df97dcb7 100644
--- a/src/vm/marshalnative.cpp
+++ b/src/vm/marshalnative.cpp
@@ -266,6 +266,11 @@ FCIMPL1(FC_BOOL_RET, MarshalNative::IsPinnable, Object* obj)
if (obj->GetMethodTable() == g_pStringClass)
FC_RETURN_BOOL(TRUE);
+#ifdef FEATURE_UTF8STRING
+ if (obj->GetMethodTable() == g_pUtf8StringClass)
+ FC_RETURN_BOOL(TRUE);
+#endif // FEATURE_UTF8STRING
+
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF)ObjectToOBJECTREF(obj);
@@ -527,6 +532,11 @@ void ValidatePinnedObject(OBJECTREF obj)
if (obj->GetMethodTable() == g_pStringClass)
return;
+#ifdef FEATURE_UTF8STRING
+ if (obj->GetMethodTable() == g_pUtf8StringClass)
+ return;
+#endif // FEATURE_UTF8STRING
+
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF) obj;
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 5321fd3ee3..5e0a821e44 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -402,6 +402,7 @@ DEFINE_METASIG(IM(Bool_Bool_RetStr, F F, s))
DEFINE_METASIG(IM(PtrChar_RetVoid, P(u), v))
DEFINE_METASIG(IM(PtrChar_Int_Int_RetVoid, P(u) i i, v))
+DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetVoid, GI(g(READONLY_SPAN), 1, b), v))
DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetVoid, GI(g(READONLY_SPAN), 1, u), v))
DEFINE_METASIG(IM(PtrSByt_RetVoid, P(B), v))
DEFINE_METASIG(IM(PtrSByt_Int_Int_RetVoid, P(B) i i, v))
@@ -420,6 +421,19 @@ DEFINE_METASIG(IM(PtrSByt_Int_Int_RetStr, P(B) i i, s))
DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))
+DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v))
+DEFINE_METASIG(IM(PtrByte_RetVoid, P(b), v))
+
+#ifdef FEATURE_UTF8STRING
+DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetUtf8Str, GI(g(READONLY_SPAN), 1, b), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetUtf8Str, GI(g(READONLY_SPAN), 1, u), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ArrByte_Int_Int_RetUtf8Str, a(b) i i, C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(PtrByte_RetUtf8Str, P(b), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ArrChar_Int_Int_RetUtf8Str, a(u) i i, C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(PtrChar_RetUtf8Str, P(u), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(String_RetUtf8Str, s, C(UTF8_STRING)))
+#endif // FEATURE_UTF8STRING
+
DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v))
DEFINE_METASIG_T(SM(RetCultureInfo, _, C(CULTURE_INFO)))
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index 9f9b25e37b..84f8399dc2 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -1743,7 +1743,7 @@ public:
BOOL IsString()
{
LIMITED_METHOD_DAC_CONTRACT;
- return HasComponentSize() && !IsArray();
+ return HasComponentSize() && !IsArray() && RawGetComponentSize() == 2;
}
BOOL HasComponentSize() const
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 568a23136e..d4ce5b0df0 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -9711,6 +9711,19 @@ void MethodTableBuilder::CheckForSystemTypes()
pMT->SetComponentSize(2);
}
+#ifdef FEATURE_UTF8STRING
+ else if (strcmp(name, g_Utf8StringName) == 0 && strcmp(nameSpace, g_SystemNS) == 0)
+ {
+ // Utf8Strings are not "normal" objects, so we need to mess with their method table a bit
+ // so that the GC can figure out how big each string is...
+ DWORD baseSize = Utf8StringObject::GetBaseSize();
+ pMT->SetBaseSize(baseSize); // NULL character included
+
+ GetHalfBakedClass()->SetBaseSizePadding(baseSize - bmtFP->NumInstanceFieldBytes);
+
+ pMT->SetComponentSize(1);
+ }
+#endif // FEATURE_UTF8STRING
else if (strcmp(name, g_CriticalFinalizerObjectName) == 0 && strcmp(nameSpace, g_ConstrainedExecutionNS) == 0)
{
// To introduce a class with a critical finalizer,
diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp
index faec6e5c97..10ed4944b6 100644
--- a/src/vm/mscorlib.cpp
+++ b/src/vm/mscorlib.cpp
@@ -84,10 +84,12 @@
#if defined(FEATURE_EVENTSOURCE_XPLAT)
#include "nativeeventsource.h"
#include "eventpipe.h"
+#include "eventpipeinternal.h"
#endif //defined(FEATURE_EVENTSOURCE_XPLAT)
#ifdef FEATURE_PERFTRACING
#include "eventpipe.h"
+#include "eventpipeinternal.h"
#endif //FEATURE_PERFTRACING
#endif // CROSSGEN_MSCORLIB
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index aea2197f9a..c54a635abe 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -320,6 +320,12 @@ DEFINE_FIELD(ENC_HELPER, OBJECT_REFERENCE, _objectReference)
DEFINE_CLASS(ENCODING, Text, Encoding)
+DEFINE_CLASS(RUNE, Text, Rune)
+
+#ifdef FEATURE_UTF8STRING
+DEFINE_CLASS(CHAR8, System, Char8)
+#endif // FEATURE_UTF8STRING
+
DEFINE_CLASS(ENUM, System, Enum)
DEFINE_CLASS(ENVIRONMENT, System, Environment)
@@ -698,6 +704,7 @@ DEFINE_METHOD(RUNTIME_HELPERS, PREPARE_CONSTRAINED_REGIONS, PrepareConstrai
DEFINE_METHOD(RUNTIME_HELPERS, PREPARE_CONSTRAINED_REGIONS_NOOP, PrepareConstrainedRegionsNoOP, SM_RetVoid)
DEFINE_METHOD(RUNTIME_HELPERS, EXECUTE_BACKOUT_CODE_HELPER, ExecuteBackoutCodeHelper, SM_Obj_Obj_Bool_RetVoid)
DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsReferenceOrContainsReferences, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig)
DEFINE_CLASS(JIT_HELPERS, CompilerServices, JitHelpers)
DEFINE_METHOD(JIT_HELPERS, ENUM_EQUALS, EnumEquals, NoSig)
@@ -815,6 +822,17 @@ DEFINE_METHOD(STRING, WCSLEN, wcslen,
DEFINE_METHOD(STRING, STRLEN, strlen, SM_PtrByte_RetInt)
DEFINE_PROPERTY(STRING, LENGTH, Length, Int)
+#ifdef FEATURE_UTF8STRING
+DEFINE_CLASS(UTF8_STRING, System, Utf8String)
+DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFBYTE,Ctor, IM_ReadOnlySpanOfByte_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFCHAR,Ctor, IM_ReadOnlySpanOfChar_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_BYTEARRAY_START_LEN,Ctor, IM_ArrByte_Int_Int_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_BYTEPTR, Ctor, IM_PtrByte_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_CHARARRAY_START_LEN,Ctor, IM_ArrChar_Int_Int_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_CHARPTR, Ctor, IM_PtrChar_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_STRING, Ctor, IM_String_RetUtf8Str)
+#endif // FEATURE_UTF8STRING
+
DEFINE_CLASS(STRING_BUILDER, Text, StringBuilder)
DEFINE_PROPERTY(STRING_BUILDER, LENGTH, Length, Int)
DEFINE_PROPERTY(STRING_BUILDER, CAPACITY, Capacity, Int)
diff --git a/src/vm/object.h b/src/vm/object.h
index 6bc3a74471..9087afa4a5 100644
--- a/src/vm/object.h
+++ b/src/vm/object.h
@@ -35,7 +35,10 @@ void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref);
* | sync block index, which is at a negative offset
* |
* +-- code:StringObject - String objects are specialized objects for string
- * | storage/retrieval for higher performance
+ * | storage/retrieval for higher performance (UCS-2 / UTF-16 data)
+ * |
+ * +-- code:Utf8StringObject - String objects are specialized objects for string
+ * | storage/retrieval for higher performance (UTF-8 data)
* |
* +-- BaseObjectWithCachedData - Object Plus one object field for caching.
* | |
@@ -870,6 +873,9 @@ typedef DPTR(UPTRArray) PTR_UPTRArray;
typedef DPTR(PTRArray) PTR_PTRArray;
class StringObject;
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject;
+#endif // FEATURE_UTF8STRING
#ifdef USE_CHECKED_OBJECTREFS
typedef REF<ArrayBase> BASEARRAYREF;
@@ -888,6 +894,9 @@ typedef REF<UPTRArray> UPTRARRAYREF;
typedef REF<CHARArray> CHARARRAYREF;
typedef REF<PTRArray> PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef REF<StringObject> STRINGREF;
+#ifdef FEATURE_UTF8STRING
+typedef REF<Utf8StringObject> UTF8STRINGREF;
+#endif // FEATURE_UTF8STRING
#else // USE_CHECKED_OBJECTREFS
@@ -907,6 +916,9 @@ typedef PTR_UPTRArray UPTRARRAYREF;
typedef PTR_CHARArray CHARARRAYREF;
typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef PTR_StringObject STRINGREF;
+#ifdef FEATURE_UTF8STRING
+typedef PTR_Utf8StringObject UTF8STRINGREF;
+#endif // FEATURE_UTF8STRING
#endif // USE_CHECKED_OBJECTREFS
@@ -1199,6 +1211,56 @@ public:
};
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject : public Object
+{
+#ifdef DACCESS_COMPILE
+ friend class ClrDataAccess;
+#endif
+
+private:
+ DWORD m_StringLength;
+ BYTE m_FirstChar;
+
+public:
+ VOID SetLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; }
+
+protected:
+ Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
+ ~Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
+
+public:
+
+ /*=================RefInterpretGetStringValuesDangerousForGC======================
+ **N.B.: This perfoms no range checking and relies on the caller to have done this.
+ **Args: (IN)ref -- the Utf8String to be interpretted.
+ ** (OUT)chars -- a pointer to the characters in the buffer.
+ ** (OUT)length -- a pointer to the length of the buffer.
+ **Returns: void.
+ **Exceptions: None.
+ ==============================================================================*/
+ // !!!! If you use this function, you have to be careful because chars is a pointer
+ // !!!! to the data buffer of ref. If GC happens after this call, you need to make
+ // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref.
+ void RefInterpretGetStringValuesDangerousForGC(__deref_out_ecount(*length + 1) CHAR **chars, int *length) {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(GetGCSafeMethodTable() == g_pUtf8StringClass);
+ *length = GetStringLength();
+ *chars = GetBuffer();
+#ifdef _DEBUG
+ EnableStressHeapHelper();
+#endif
+ }
+
+ DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );}
+ CHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; _ASSERTE(this != nullptr); return (CHAR*)( dac_cast<TADDR>(this) + offsetof(Utf8StringObject, m_FirstChar) ); }
+
+ static DWORD GetBaseSize();
+ static SIZE_T GetSize(DWORD stringLength);
+};
+#endif // FEATURE_UTF8STRING
+
// This is the Method version of the Reflection object.
// A Method has adddition information.
// m_pMD - A pointer to the actual MethodDesc of the method.
diff --git a/src/vm/object.inl b/src/vm/object.inl
index 9652909250..ebf9d364c9 100644
--- a/src/vm/object.inl
+++ b/src/vm/object.inl
@@ -71,6 +71,22 @@ __forceinline /*static*/ SIZE_T StringObject::GetSize(DWORD strLen)
return GetBaseSize() + strLen * sizeof(WCHAR);
}
+#ifdef FEATURE_UTF8STRING
+__forceinline /*static*/ DWORD Utf8StringObject::GetBaseSize()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return OBJECT_BASESIZE + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */;
+}
+
+__forceinline /*static*/ SIZE_T Utf8StringObject::GetSize(DWORD strLen)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return GetBaseSize() + strLen;
+}
+#endif // FEATURE_UTF8STRING
+
#ifdef DACCESS_COMPILE
inline void Object::EnumMemoryRegions(void)
diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp
index 854288847e..663b82f403 100644
--- a/src/vm/proftoeeinterfaceimpl.cpp
+++ b/src/vm/proftoeeinterfaceimpl.cpp
@@ -9661,12 +9661,12 @@ HRESULT ProfToEEInterfaceImpl::ApplyMetaData(
CONTRACTL
{
NOTHROW;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
- PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(kP2EEAllowableAfterAttach, (LF_CORPROF, LL_INFO1000, "**PROF: ApplyMetaData.\n"));
+ PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(kP2EEAllowableAfterAttach | kP2EETriggers, (LF_CORPROF, LL_INFO1000, "**PROF: ApplyMetaData.\n"));
if (moduleId == NULL)
{
diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp
index 1f8aa04593..954d6ae267 100644
--- a/src/vm/reflectioninvocation.cpp
+++ b/src/vm/reflectioninvocation.cpp
@@ -1001,6 +1001,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// Skip the activation optimization for remoting because of remoting proxy is not always activated.
// It would be nice to clean this up and get remoting to always activate methodtable behind the proxy.
BOOL fForceActivationForRemoting = FALSE;
+ BOOL fCtorOfVariableSizedObject = FALSE;
if (fConstructor)
{
@@ -1018,7 +1019,8 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
MethodTable * pMT = ownerType.AsMethodTable();
{
- if (pMT != g_pStringClass)
+ fCtorOfVariableSizedObject = pMT->HasComponentSize();
+ if (!fCtorOfVariableSizedObject)
gc.retVal = pMT->Allocate();
}
}
@@ -1324,7 +1326,11 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
if (fConstructor)
{
// We have a special case for Strings...The object is returned...
- if (ownerType == TypeHandle(g_pStringClass)) {
+ if (ownerType == TypeHandle(g_pStringClass)
+#ifdef FEATURE_UTF8STRING
+ || ownerType == TypeHandle(g_pUtf8StringClass)
+#endif // FEATURE_UTF8STRING
+ ) {
PVOID pReturnValue = &callDescrData.returnValue;
gc.retVal = *(OBJECTREF *)pReturnValue;
}
@@ -2590,8 +2596,12 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa
MethodTable *pMT = type.GetMethodTable();
PREFIX_ASSUME(pMT != NULL);
- //We don't allow unitialized strings.
- if (pMT == g_pStringClass) {
+ //We don't allow unitialized Strings or Utf8Strings.
+ if (pMT == g_pStringClass
+#ifdef FEATURE_UTF8STRING
+ || pMT == g_pUtf8StringClass
+#endif // FEATURE_UTF8STRING
+ ) {
COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings"));
}
diff --git a/src/vm/runtimecallablewrapper.cpp b/src/vm/runtimecallablewrapper.cpp
index 51c2ee016f..48554086da 100644
--- a/src/vm/runtimecallablewrapper.cpp
+++ b/src/vm/runtimecallablewrapper.cpp
@@ -491,27 +491,6 @@ IClassFactory *ComClassFactory::GetIClassFactory()
else
{
// No server name is specified so we use CLSCTX_SERVER.
-
-#ifdef FEATURE_CLASSIC_COMINTEROP
- // If the CLSID is hosted by the CLR itself, then we do not want to go through the COM registration
- // entries, as this will trigger our COM activation code that may not activate against this runtime.
- // In this scenario, we want to get the address of the DllGetClassObject method on this CLR or a DLL
- // that lives in the same directory as the CLR and use it directly. The code falls back to
- // CoGetClassObject if we fail on the call to DllGetClassObject, but it might be better to fail outright.
- if (Clr::Util::Com::CLSIDHasMscoreeAsInprocServer32(m_rclsid))
- {
- typedef HRESULT (STDMETHODCALLTYPE *PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
-
- StackSString ssServer;
- if (FAILED(Clr::Util::Com::FindServerUsingCLSID(m_rclsid, ssServer)))
- {
- }
- else
- {
- }
- }
-#endif // FEATURE_CLASSIC_COMINTEROP
-
if (pClassFactory == NULL)
hr = CoGetClassObject(m_rclsid, CLSCTX_SERVER, NULL, IID_IClassFactory, (void**)&pClassFactory);
}
diff --git a/src/vm/synch.cpp b/src/vm/synch.cpp
index 55d42d73f5..e49428e450 100644
--- a/src/vm/synch.cpp
+++ b/src/vm/synch.cpp
@@ -18,7 +18,7 @@ void CLREventBase::CreateAutoEvent (BOOL bInitialState // If TRUE, initial stat
THROWS;
GC_NOTRIGGER;
// disallow creation of Crst before EE starts
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
PRECONDITION((!IsOSEvent()));
}
@@ -44,7 +44,7 @@ BOOL CLREventBase::CreateAutoEventNoThrow (BOOL bInitialState // If TRUE, initi
NOTHROW;
GC_NOTRIGGER;
// disallow creation of Crst before EE starts
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
PRECONDITION((!IsOSEvent()));
}
@@ -70,7 +70,7 @@ void CLREventBase::CreateManualEvent (BOOL bInitialState // If TRUE, initial st
THROWS;
GC_NOTRIGGER;
// disallow creation of Crst before EE starts
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
PRECONDITION((!IsOSEvent()));
}
@@ -93,7 +93,7 @@ BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState // If TRUE, ini
NOTHROW;
GC_NOTRIGGER;
// disallow creation of Crst before EE starts
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
PRECONDITION((!IsOSEvent()));
}
@@ -220,7 +220,7 @@ void CLREventBase::CreateOSAutoEvent (BOOL bInitialState // If TRUE, initial st
}
CONTRACTL_END;
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
//_ASSERTE (g_fEEStarted);
SetOSEvent();
@@ -269,7 +269,7 @@ void CLREventBase::CreateOSManualEvent (BOOL bInitialState // If TRUE, initial
}
CONTRACTL_END;
- // Can not assert here. ASP.Net uses our Threadpool before EE is started.
+ // Can not assert here. ASP.NET uses our Threadpool before EE is started.
//_ASSERTE (g_fEEStarted);
SetOSEvent();
diff --git a/src/vm/threadpoolrequest.h b/src/vm/threadpoolrequest.h
index 17df7a790f..0eb037523e 100644
--- a/src/vm/threadpoolrequest.h
+++ b/src/vm/threadpoolrequest.h
@@ -163,7 +163,7 @@ public:
private:
ADID m_id;
TPIndex m_index;
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_numRequestsPending;
@@ -256,7 +256,7 @@ public:
private:
SpinLock m_lock;
ULONG m_NumRequests;
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_outstandingThreadRequestCount;
diff --git a/src/vm/vars.cpp b/src/vm/vars.cpp
index 179acda8af..8b329d4c2e 100644
--- a/src/vm/vars.cpp
+++ b/src/vm/vars.cpp
@@ -61,6 +61,9 @@ GPTR_IMPL(MethodTable, g_pObjectClass);
GPTR_IMPL(MethodTable, g_pRuntimeTypeClass);
GPTR_IMPL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_IMPL(MethodTable, g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+GPTR_IMPL(MethodTable, g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
GPTR_IMPL(MethodTable, g_pArrayClass);
GPTR_IMPL(MethodTable, g_pSZArrayHelperClass);
GPTR_IMPL(MethodTable, g_pNullableClass);
diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp
index 91ad42a91c..d8ffc60e25 100644
--- a/src/vm/vars.hpp
+++ b/src/vm/vars.hpp
@@ -79,6 +79,9 @@ class LoaderHeap;
class IGCHeap;
class Object;
class StringObject;
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject;
+#endif // FEATURE_UTF8STRING
class ArrayClass;
class MethodTable;
class MethodDesc;
@@ -313,6 +316,10 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((objref).operator-> ())
#define ObjectToSTRINGREF(obj) (STRINGREF(obj))
#define STRINGREFToObject(objref) (*( (StringObject**) &(objref) ))
+#ifdef FEATURE_UTF8STRING
+#define ObjectToUTF8STRINGREF(obj) (UTF8STRINGREF(obj))
+#define UTF8STRINGREFToObject(objref) (*( (Utf8StringObject**) &(objref) ))
+#endif // FEATURE_UTF8STRING
#else // _DEBUG_IMPL
@@ -323,6 +330,10 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((PTR_Object) (objref))
#define ObjectToSTRINGREF(obj) ((PTR_StringObject) (obj))
#define STRINGREFToObject(objref) ((PTR_StringObject) (objref))
+#ifdef FEATURE_UTF8STRING
+#define ObjectToUTF8STRINGREF(obj) ((PTR_Utf8StringObject) (obj))
+#define UTF8STRINGREFToObject(objref) ((PTR_Utf8StringObject) (objref))
+#endif // FEATURE_UTF8STRING
#endif // _DEBUG_IMPL
@@ -363,6 +374,9 @@ GPTR_DECL(MethodTable, g_pObjectClass);
GPTR_DECL(MethodTable, g_pRuntimeTypeClass);
GPTR_DECL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_DECL(MethodTable, g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+GPTR_DECL(MethodTable, g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
GPTR_DECL(MethodTable, g_pArrayClass);
GPTR_DECL(MethodTable, g_pSZArrayHelperClass);
GPTR_DECL(MethodTable, g_pNullableClass);
diff --git a/src/zap/zapimport.cpp b/src/zap/zapimport.cpp
index d84ea79e8a..9bdc1d9499 100644
--- a/src/zap/zapimport.cpp
+++ b/src/zap/zapimport.cpp
@@ -1831,7 +1831,7 @@ ZapImport * ZapImportTable::GetDictionaryLookupCell(CORCOMPILE_FIXUP_BLOB_KIND k
ThrowHR(E_NOTIMPL);
}
- _ASSERTE(((DWORD)pResolvedToken->tokenContext & 1) == 0);
+ _ASSERTE(((DWORD)(SIZE_T)pResolvedToken->tokenContext & 1) == 0);
return GetImportForSignature<ZapDynamicHelperCell, ZapNodeType_DynamicHelperCell>((void*)pResolvedToken->tokenContext, &sigBuilder);
}
@@ -1894,12 +1894,12 @@ public:
ReadyToRunHelper GetReadyToRunHelper()
{
- return (ReadyToRunHelper)((DWORD)GetHandle() & ~READYTORUN_HELPER_FLAG_VSD);
+ return (ReadyToRunHelper)((DWORD)(SIZE_T)GetHandle() & ~READYTORUN_HELPER_FLAG_VSD);
}
DWORD GetSectionIndex()
{
- return (DWORD)GetHandle2();
+ return (DWORD)(SIZE_T)GetHandle2();
}
BOOL IsDelayLoadHelper()
@@ -1919,7 +1919,7 @@ public:
BOOL IsVSD()
{
- return ((DWORD)GetHandle() & READYTORUN_HELPER_FLAG_VSD) != 0;
+ return ((DWORD)(SIZE_T)GetHandle() & READYTORUN_HELPER_FLAG_VSD) != 0;
}
BOOL IsLazyHelper()
diff --git a/tests/CoreFX/CoreFX.issues.json b/tests/CoreFX/CoreFX.issues.json
index 3db01ef4e1..28eb92937a 100644
--- a/tests/CoreFX/CoreFX.issues.json
+++ b/tests/CoreFX/CoreFX.issues.json
@@ -1254,6 +1254,10 @@
"reason": "Needs updates for XUnit 2.4"
},
{
+ "name": "System.Tests.ArraySegment_Tests.CopyTo_Invalid",
+ "reason": "Needs parameter name updated to 'destination'."
+ },
+ {
"name": "System.Tests.TimeSpanTests.Parse_Invalid",
"reason": "Temporary disabling till merging the PR https://github.com/dotnet/corefx/pull/34561"
},
diff --git a/tests/arm64/corefx_test_exclusions.txt b/tests/arm64/corefx_test_exclusions.txt
index 19f9ef54a6..cb2939917f 100644
--- a/tests/arm64/corefx_test_exclusions.txt
+++ b/tests/arm64/corefx_test_exclusions.txt
@@ -1,12 +1,7 @@
-Invariant.Tests
+Microsoft.Win32.SystemEvents.Tests # https://github.com/dotnet/coreclr/issues/22442 -- timeout
System.ComponentModel.Composition.Tests # https://github.com/dotnet/coreclr/issues/18913
-System.Diagnostics.Process.Tests # https://github.com/dotnet/coreclr/issues/16001
-System.Drawing.Common.Tests # https://github.com/dotnet/coreclr/issues/18886
-System.Management.Tests # https://github.com/dotnet/coreclr/issues/18886
+System.Drawing.Common.Tests # https://github.com/dotnet/corefx/issues/35424
+System.Management.Tests # https://github.com/dotnet/corefx/issues/34030
System.Net.HttpListener.Tests # https://github.com/dotnet/coreclr/issues/17584
-System.Numerics.Vectors.Tests # https://github.com/dotnet/coreclr/issues/18886
-System.Runtime.InteropServices.RuntimeInformation.Tests # VM assert -- https://github.com/dotnet/coreclr/issues/18886
-System.Runtime.Serialization.Formatters.Tests # long running? https://github.com/dotnet/coreclr/issues/18886
System.Runtime.Tests # https://github.com/dotnet/coreclr/issues/18914
System.Text.RegularExpressions.Tests # https://github.com/dotnet/coreclr/issues/18912 -- timeout -- JitMinOpts only
-System.ValueTuple.Tests
diff --git a/tests/dir.props b/tests/dir.props
index bef7afc53e..cb56369af5 100644
--- a/tests/dir.props
+++ b/tests/dir.props
@@ -19,7 +19,7 @@
Switching to the .NET Core version of the BuildTools tasks seems to break numerous scenarios, such as VS intellisense and resource designer
as well as running the build on mono. Until we can get these sorted out we will continue using the .NET 4.5 version of the tasks.
- It also breaks building any C# project with dotnet.exe on Windows. The windows version of BuildTools doesn't appear to download the .Net Core
+ It also breaks building any C# project with dotnet.exe on Windows. The windows version of BuildTools doesn't appear to download the .NET Core
Roslyn NuGet package which has a Csc task supporting OverrideToolHost, but the default BuildTools CSharpCore targets file does specify
OverrideToolHost. The result is that building anything in C# when RunningOnCore=true on Windows fails in Csc task with a parameter not supported error.
diff --git a/tests/scripts/format.py b/tests/scripts/format.py
index b622cf4625..5ed90968c9 100644
--- a/tests/scripts/format.py
+++ b/tests/scripts/format.py
@@ -69,7 +69,7 @@ def main(argv):
my_env = os.environ
- # Download .Net CLI
+ # Download .NET CLI
dotnetcliUrl = ""
dotnetcliFilename = ""
@@ -88,7 +88,7 @@ def main(argv):
if not os.path.isdir(dotnetcliPath):
raise
- print("Downloading .Net CLI")
+ print("Downloading .NET CLI")
if platform == 'Linux':
dotnetcliUrl = "https://dotnetcli.azureedge.net/dotnet/Sdk/2.1.402/dotnet-sdk-2.1.402-linux-x64.tar.gz"
dotnetcliFilename = os.path.join(dotnetcliPath, 'dotnetcli-jitutils.tar.gz')
@@ -106,10 +106,10 @@ def main(argv):
urlretrieve(dotnetcliUrl, dotnetcliFilename)
if not os.path.isfile(dotnetcliFilename):
- print("Did not download .Net CLI!")
+ print("Did not download .NET CLI!")
return -1
- # Install .Net CLI
+ # Install .NET CLI
if platform == 'Linux' or platform == 'OSX':
tar = tarfile.open(dotnetcliFilename)
@@ -127,7 +127,7 @@ def main(argv):
if not os.path.isfile(os.path.join(dotnetcliPath, dotnet)):
- print("Did not extract .Net CLI from download")
+ print("Did not extract .NET CLI from download")
return -1
# Download bootstrap
diff --git a/tests/scripts/run-pmi-diffs.py b/tests/scripts/run-pmi-diffs.py
index d1e5954b93..e02f409dc3 100755
--- a/tests/scripts/run-pmi-diffs.py
+++ b/tests/scripts/run-pmi-diffs.py
@@ -437,7 +437,7 @@ def do_pmi_diffs():
# Download .NET CLI
- log('Downloading .Net CLI')
+ log('Downloading .NET CLI')
dotnetcliUrl = ""
dotnetcliFilename = ""
@@ -469,12 +469,12 @@ def do_pmi_diffs():
urlretrieve(dotnetcliUrl, dotnetcliFilename)
if not os.path.isfile(dotnetcliFilename):
- log('ERROR: Did not download .Net CLI')
+ log('ERROR: Did not download .NET CLI')
return 1
- # Install .Net CLI
+ # Install .NET CLI
- log('Unpacking .Net CLI')
+ log('Unpacking .NET CLI')
if not testing:
if Is_windows:
@@ -486,7 +486,7 @@ def do_pmi_diffs():
tar.close()
if not os.path.isfile(os.path.join(dotnetcliPath, dotnet_tool)):
- log('ERROR: did not extract .Net CLI from download')
+ log('ERROR: did not extract .NET CLI from download')
return 1
# Add dotnet CLI to PATH we'll use to spawn processes.
diff --git a/tests/src/Interop/SizeConst/SizeConstTest.cs b/tests/src/Interop/SizeConst/SizeConstTest.cs
index a4d85c898e..b4b90552cb 100644
--- a/tests/src/Interop/SizeConst/SizeConstTest.cs
+++ b/tests/src/Interop/SizeConst/SizeConstTest.cs
@@ -26,7 +26,7 @@ class Test
{
// always marshal managedArray.Length
S_CHARArray_ByValTStr s = new S_CHARArray_ByValTStr();
- s.arr = "有个可爱";
+ s.arr = "abcd";
TakeByValTStr(s, s.arr.Length);
// off by one byte since sizeconst == 4 and
diff --git a/tests/src/JIT/Methodical/cctor/misc/testlib.cs b/tests/src/JIT/Methodical/cctor/misc/testlib.cs
index c5fb330579..5035f185ad 100644
--- a/tests/src/JIT/Methodical/cctor/misc/testlib.cs
+++ b/tests/src/JIT/Methodical/cctor/misc/testlib.cs
@@ -33,7 +33,7 @@ namespace Precise
}
Console.WriteLine("in .cctor(), measure.a is {0}", measure.a);
Console.WriteLine("Current thread is {0}", Thread.CurrentThread.Name);
- // Following two lines commented because not available in .Net Core
+ // Following two lines commented because not available in .NET Core
// Console.WriteLine("Calling assembly is {0}", Assembly.GetCallingAssembly().FullName);
// Console.WriteLine("This assembly is {0}", Assembly.GetExecutingAssembly().FullName);
measure.a += 8;
diff --git a/tests/src/JIT/Performance/CodeQuality/BenchmarksGame/regex-redux/regex-redux-5.cs b/tests/src/JIT/Performance/CodeQuality/BenchmarksGame/regex-redux/regex-redux-5.cs
index 841db0897a..47e2bace0a 100644
--- a/tests/src/JIT/Performance/CodeQuality/BenchmarksGame/regex-redux/regex-redux-5.cs
+++ b/tests/src/JIT/Performance/CodeQuality/BenchmarksGame/regex-redux/regex-redux-5.cs
@@ -29,7 +29,7 @@ namespace BenchmarksGame
{
static Regex regex(string re)
{
- // Not compiled on .Net Core, hence poor benchmark results.
+ // Not compiled on .NET Core, hence poor benchmark results.
return new Regex(re, RegexOptions.Compiled);
}
diff --git a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericClasses.csproj b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericClasses.csproj
index f376265528..aff76cd327 100644
--- a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericClasses.csproj
+++ b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericClasses.csproj
@@ -13,7 +13,7 @@
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>1</CLRTestPriority>
- <!-- Test fails to build on .Net Core 3.0 preview3 -->
+ <!-- Test fails to build on .NET Core 3.0 preview3 -->
<DisableProjectBuild Condition="'$(__BuildOS)' == 'OSX'">true</DisableProjectBuild>
</PropertyGroup>
<ItemGroup>
diff --git a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericStructs.csproj b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericStructs.csproj
index e3a657319a..c608d69e59 100644
--- a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericStructs.csproj
+++ b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericStructs.csproj
@@ -13,7 +13,7 @@
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>1</CLRTestPriority>
- <!-- Test fails to build on .Net Core 3.0 preview3 -->
+ <!-- Test fails to build on .NET Core 3.0 preview3 -->
<DisableProjectBuild Condition="'$(__BuildOS)' == 'OSX'">true</DisableProjectBuild>
</PropertyGroup>
<ItemGroup>
diff --git a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericTypesMix.csproj b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericTypesMix.csproj
index 7d59baaa49..8099668004 100644
--- a/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericTypesMix.csproj
+++ b/tests/src/Loader/classloader/generics/Instantiation/Nesting/NestedGenericTypesMix.csproj
@@ -13,7 +13,7 @@
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>1</CLRTestPriority>
- <!-- Test fails to build on .Net Core 3.0 preview3 -->
+ <!-- Test fails to build on .NET Core 3.0 preview3 -->
<DisableProjectBuild Condition="'$(__BuildOS)' == 'OSX'">true</DisableProjectBuild>
</PropertyGroup>
diff --git a/tests/src/Regressions/coreclr/22728/createdelegate.il b/tests/src/Regressions/coreclr/22728/createdelegate.il
deleted file mode 100644
index 2aa0a614c2..0000000000
--- a/tests/src/Regressions/coreclr/22728/createdelegate.il
+++ /dev/null
@@ -1,103 +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.
-
-.assembly extern System.Runtime { }
-.assembly extern System.Console { }
-.assembly createdelegate { }
-
-.class private auto ansi beforefieldinit A
- extends [System.Runtime]System.Object
- implements B,
- C,
- D
-{
- .method private hidebysig newslot virtual final
- instance char B.F1(int32 pA) cil managed
- {
- .override B::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig newslot virtual final
- instance char D.F1(int32 pA) cil managed
- {
- .override D::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig instance int32
- Test() cil managed
- {
- ldtoken class [System.Runtime]System.Func`2<int32,char>
- call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
- ldarg.0
- ldtoken method instance char D::F1(int32)
- call class [System.Runtime]System.Reflection.MethodBase [System.Runtime]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [System.Runtime]System.RuntimeMethodHandle)
- castclass class [System.Runtime]System.Reflection.MethodInfo
- call class [System.Runtime]System.Delegate [System.Runtime]System.Delegate::CreateDelegate(class [System.Runtime]System.Type, object, class [System.Runtime]System.Reflection.MethodInfo)
- castclass class [System.Runtime]System.Func`2<int32,char>
-
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 65
- ceq
- brtrue A_F1_OK
-
- ldc.i4.3
- ret
-
-A_F1_OK:
-
- ldc.i4.s 100
-
- ret
- }
-
- .method private hidebysig static int32 Main() cil managed
- {
- .entrypoint
- newobj instance void A::.ctor()
- call instance int32 A::Test()
- ret
- }
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- ldarg.0
- call instance void [System.Runtime]System.Object::.ctor()
- ret
- }
-}
-
-.class interface private abstract auto ansi B
- implements C,
- D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pB) cil managed
- {
- ldc.i4.s 66
- ret
- }
-}
-
-.class interface private abstract auto ansi C
- implements D
-{
-}
-
-.class interface private abstract auto ansi D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pD) cil managed
- {
- ldc.i4.s 68
- ret
- }
-}
diff --git a/tests/src/Regressions/coreclr/22728/createdelegate.ilproj b/tests/src/Regressions/coreclr/22728/createdelegate.ilproj
deleted file mode 100644
index d218608b54..0000000000
--- a/tests/src/Regressions/coreclr/22728/createdelegate.ilproj
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
- <PropertyGroup>
- <AssemblyName>$(MSBuildProjectName)</AssemblyName>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
- <OutputType>Exe</OutputType>
- <CLRTestKind>BuildAndRun</CLRTestKind>
- <CLRTestPriority>0</CLRTestPriority>
- </PropertyGroup>
-
- <ItemGroup>
- <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
- <Visible>False</Visible>
- </CodeAnalysisDependentAssemblyPaths>
- </ItemGroup>
-
- <ItemGroup>
- <Compile Include="createdelegate.il" />
- </ItemGroup>
-
-
- <ItemGroup>
- <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
- </ItemGroup>
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
diff --git a/tests/src/Regressions/coreclr/22728/debug15.il b/tests/src/Regressions/coreclr/22728/debug15.il
deleted file mode 100644
index a545c9b2cf..0000000000
--- a/tests/src/Regressions/coreclr/22728/debug15.il
+++ /dev/null
@@ -1,133 +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.
-
-.assembly extern System.Runtime { }
-.assembly extern System.Console { }
-.assembly debug15 { }
-
-.class private auto ansi beforefieldinit A
- extends [System.Runtime]System.Object
- implements B,
- C,
- D
-{
- .method private hidebysig newslot virtual final
- instance char B.F1(int32 pA) cil managed
- {
- .override B::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig newslot virtual final
- instance char D.F1(int32 pA) cil managed
- {
- .override D::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig instance int32
- Test() cil managed
- {
- ldarg.0
- ldftn instance char B::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 66
- ceq
- brtrue B_F1_OK
-
- ldc.i4.1
- ret
-
-B_F1_OK:
-
- ldarg.0
- ldftn instance char D::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 68
- ceq
- brtrue D_F1_OK
-
- ldc.i4.2
- ret
-
-D_F1_OK:
-
- ldarg.0
- dup
- ldvirtftn instance char D::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 65
- ceq
- brtrue A_F1_OK
-
- ldc.i4.3
- ret
-
-A_F1_OK:
-
- ldc.i4.s 100
-
- ret
- }
-
- .method private hidebysig static int32 Main() cil managed
- {
- .entrypoint
- newobj instance void A::.ctor()
- call instance int32 A::Test()
- ret
- }
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- ldarg.0
- call instance void [System.Runtime]System.Object::.ctor()
- ret
- }
-}
-
-.class interface private abstract auto ansi B
- implements C,
- D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pB) cil managed
- {
- ldc.i4.s 66
- ret
- }
-}
-
-.class interface private abstract auto ansi C
- implements D
-{
-}
-
-.class interface private abstract auto ansi D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pD) cil managed
- {
- ldc.i4.s 68
- ret
- }
-}
diff --git a/tests/src/Regressions/coreclr/22728/debug15.ilproj b/tests/src/Regressions/coreclr/22728/debug15.ilproj
deleted file mode 100644
index 442a59cc4e..0000000000
--- a/tests/src/Regressions/coreclr/22728/debug15.ilproj
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
- <PropertyGroup>
- <AssemblyName>$(MSBuildProjectName)</AssemblyName>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
- <OutputType>Exe</OutputType>
- <CLRTestKind>BuildAndRun</CLRTestKind>
- <CLRTestPriority>0</CLRTestPriority>
- </PropertyGroup>
-
- <ItemGroup>
- <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
- <Visible>False</Visible>
- </CodeAnalysisDependentAssemblyPaths>
- </ItemGroup>
-
- <ItemGroup>
- <Compile Include="debug15.il" />
- </ItemGroup>
-
-
- <ItemGroup>
- <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
- </ItemGroup>
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
diff --git a/tests/src/performance/Scenario/JitBench/unofficial_dotnet/README.md b/tests/src/performance/Scenario/JitBench/unofficial_dotnet/README.md
index fd86d7bf37..bc961abd67 100644
--- a/tests/src/performance/Scenario/JitBench/unofficial_dotnet/README.md
+++ b/tests/src/performance/Scenario/JitBench/unofficial_dotnet/README.md
@@ -185,4 +185,4 @@ CoreCLR CI machines don't currently support building netcoreapp2.0 projects auth
C:\Program Files\dotnet\sdk\1.1.0\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.TargetFrameworkInference.targets(112,5): error : The current .NET SDK does not support targeting .NET Core 2.0. Either target .NET Core 1.1 or lower, or use a version of the .NET SDK that supports .NET Core 2.0. [D:\j\workspace\x64_checked_w---eac6a79c\tests\src\performance\Scenario\JitBench\JitBench.csproj]
-I assume the CI machines have fairly old SDK tools installed but I didn't have enough time to keep investigating these build issues. From I can tell if you have .Net Core 2.0+ SDK installed on your machine this build works fine from the command line and from VS.
+I assume the CI machines have fairly old SDK tools installed but I didn't have enough time to keep investigating these build issues. From I can tell if you have .NET Core 2.0+ SDK installed on your machine this build works fine from the command line and from VS.
diff --git a/tests/src/performance/linkbench/scripts/clone.cmd b/tests/src/performance/linkbench/scripts/clone.cmd
index 902fad470e..6b69c0f9f2 100644
--- a/tests/src/performance/linkbench/scripts/clone.cmd
+++ b/tests/src/performance/linkbench/scripts/clone.cmd
@@ -17,7 +17,7 @@ popd
exit /b %EXITCODE%
:DotNet
-REM We clone different versions of .Net CLI in order to cope with
+REM We clone different versions of .NET CLI in order to cope with
REM different runtimes that the benchmarks target, and certain limitations
REM in the ILLink/CLI integration and packaging.
REM