diff --git a/Directory.Build.props b/Directory.Build.props
index 7efd3a5729169..e2c0de947f336 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -83,6 +83,7 @@
$([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll'))
$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))
$([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)'))
@@ -202,6 +203,17 @@
true
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref'))
+ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data'))
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)'))
+ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)'))
+ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native'))
+
+
true
diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT
index b0694275b3dbb..14c806c5ca3e4 100644
--- a/THIRD-PARTY-NOTICES.TXT
+++ b/THIRD-PARTY-NOTICES.TXT
@@ -680,7 +680,7 @@ worldwide. This software is distributed without any warranty.
See .
-License for fastmod (https://github.com/lemire/fastmod)
+License for fastmod (https://github.com/lemire/fastmod) and ibm-fpgen (https://github.com/nigeltao/parse-number-fxx-test-data)
--------------------------------------
Copyright 2018 Daniel Lemire
@@ -952,3 +952,29 @@ by constants, including codegen instructions. The unsigned division incorporates
"round down" optimization per ridiculous_fish.
This is free and unencumbered software. Any copyright is dedicated to the Public Domain.
+
+
+License notice for mimalloc
+-----------------------------------
+
+MIT License
+
+Copyright (c) 2019 Microsoft Corporation, Daan Leijen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/design/coreclr/botr/type-loader.md b/docs/design/coreclr/botr/type-loader.md
index b873d8541a012..7c8b34627665b 100644
--- a/docs/design/coreclr/botr/type-loader.md
+++ b/docs/design/coreclr/botr/type-loader.md
@@ -76,21 +76,25 @@ There is a relatively small number of entry-points to the loader. Although the s
There are usually many calls to the type loader during JITting. Consider:
- object CreateClass()
- {
- return new MyClass();
- }
+```csharp
+object CreateClass()
+{
+ return new MyClass();
+}
+```
In the IL, MyClass is referred to using a metadata token. In order to generate a call to the `JIT_New` helper which takes care of the actual instantiation, the JIT will ask the type loader to load the type and return a handle to it. This handle will be then directly embedded in the JITted code as an immediate value. The fact that types and members are usually resolved and loaded at JIT time and not at run-time also explains the sometimes confusing behavior easily hit with code like this:
- object CreateClass()
- {
- try {
- return new MyClass();
- } catch (TypeLoadException) {
- return null;
- }
- }
+```csharp
+object CreateClass()
+{
+ try {
+ return new MyClass();
+ } catch (TypeLoadException) {
+ return null;
+ }
+}
+```
If `MyClass` fails to load, for example because it's supposed to be defined in another assembly and it was accidentally removed in the newest build, then this code will still throw `TypeLoadException`. The reason that the catch block did not catch it is that it never ran! The exception occurred during JITting and would only be catchable in the method that called `CreateClass` and caused it to be JITted. In addition, it may not be always obvious at which point the JITting is triggered due to inlining, so users should not expect and rely on deterministic behavior.
@@ -153,14 +157,16 @@ both the same type.
When the type loader is asked to load a specified type, identified for example by a typedef/typeref/typespec **token** and a **Module** , it does not do all the work atomically at once. The loading is done in phases instead. The reason for this is that the type usually depends on other types and requiring it to be fully loaded before it can be referred to by other types would result in infinite recursion and deadlocks. Consider:
- class A : C>
- { }
+```csharp
+class A : C>
+{ }
- class B : C>
- { }
+class B : C>
+{ }
- class C
- { }
+class C
+{ }
+```
These are valid types and apparently `A` depends on `B` and `B` depends on `A`.
@@ -195,10 +201,12 @@ A placeholder to be substituted by another type; the `T` in the declaration of `
A type being substituted for a generic parameter; the `int` in `List`. Note that a generic parameter can also be used as an argument. Consider:
- List GetList()
- {
- return new List();
- }
+```csharp
+List GetList()
+{
+ return new List();
+}
+```
The method has one generic parameter `T` which is used as a generic argument for the generic list class.
@@ -209,28 +217,38 @@ An optional requirement placed by generic parameters on its potential generic ar
1. Special constraints
- Reference type constraint - the generic argument must be a reference type (as opposed to a value type). The `class` keyword is used in C# to express this constraint.
- public class A where T : class
+ ```csharp
+ public class A where T : class
+ ```
- Value type constraint - the generic argument must be a value type different from `System.Nullable`. C# uses the `struct` keyword.
- public class A where T : struct
+ ```csharp
+ public class A where T : struct
+ ```
- Default constructor constraint - the generic argument must have a public parameterless constructor. This is expressed by `new()` in C#.
- public class A where T : new()
+ ```csharp
+ public class A where T : new()
+ ```
2. Base type constraints - the generic argument must be derived from
(or directly be of) the given non-interface type. It obviously makes
sense to use only zero or one reference type as a base types
constraint.
- public class A where T : EventArgs
+ ```csharp
+ public class A where T : EventArgs
+ ```
3. Implemented interface constraints - the generic argument must
implement (or directly be of) the given interface type. Zero or more
interfaces can be given.
- public class A where T : ICloneable, IComparable
+ ```csharp
+ public class A where T : ICloneable, IComparable
+ ```
The above constraints are combined with an implicit AND, i.e. a
generic parameter can be constrained to be derived from a given type,
@@ -239,11 +257,13 @@ generic parameters of the declaring type can be used to express the
constraints, introducing interdependencies among the parameters. For
example:
- public class A
- where S : T
- where T : IList {
- void f(V v) where V : S {}
- }
+```csharp
+public class A
+ where S : T
+ where T : IList {
+ void f(V v) where V : S {}
+}
+```
**Instantiation**
@@ -259,7 +279,9 @@ declared. There exists exactly one typical instantiation for each
generic type and method. Usually when one talks about an open generic
type, they have the typical instantiation in mind. Example:
- public class A {}
+```csharp
+public class A {}
+```
The C# `typeof(A<,,>)` compiles to ldtoken A\'3 which makes the
runtime load ``A`3`` instantiated at `S` , `T` , `U`.
diff --git a/docs/design/features/cross-dac.md b/docs/design/features/cross-dac.md
new file mode 100644
index 0000000000000..30becca5be626
--- /dev/null
+++ b/docs/design/features/cross-dac.md
@@ -0,0 +1,97 @@
+# Cross DAC Notes
+
+The `crossdac` is a cross-compiled DAC. It is compiled to execute on one platform, but debug a target of a different architecture.
+
+Our current crossdacs are all:
+
+- compiled to run on Windows
+- Same bitness. (Target and host have the same number of bits.
+- target a *nix variant
+
+The crossdac allow us to use Windows debugging tools to debug dumps from *nix processes.
+
+## Design
+
+### Limitations
+
+- To avoid solving remoting and synchronization issues, the crossdac will not support live processes. Only dump debugging is supported.
+- Similar to the DAC, each cross DAC must match its runtime. The DACs are indexed on a symbol server to allow the debuggers to get these as needed.
+
+### Conditional Code Selection
+
+This is a simple cross compilation of the DAC, `C++` code. This mean the `HOST_*` and the `TARGET_*` are configured differently. In this context:
+
+- `HOST` refers to the architecture of the platform that is running the debugger.
+- `TARGET` refers to the platform that generated the code dump.
+
+In general, most code should be conditioned on `TARGET_*` variables. This is because in general we want the `DAC` to behave identically when cross compiled.
+
+Code must be conditioned on `HOST` when it refers to host needed services. These have typically been thing like file i/o and memory allocation.
+
+Initial implementation allowed the compiler to find most of these. The strategy was to assume all code should be conditioned on `TARGET` and let the compiler gripe.
+
+### Type Layout
+
+The DAC is essentially a memory parsing tool with supporting functionality. The layout of types in the DAC must match the layout of types in the runtime.
+
+The `C++` standard is not explicit about all layout rules of data structures. Due to its historical evolution from `C`, most structures are arranged in an intuitive easily understood fashion. Newer and more exotic structures are less consistent.
+
+Experimentation has shown that layout varies in inheritance cases. The DAC does not support general multiple inheritance, so that simplifies things. It does support multiple inheritance with the empty base classes.
+
+These cases have proven to be problematic:
+
+- Classes with empty base classes. (I the only issue is with multiple base classes.)
+ - By default `gcc` use an empty base class optimization to eliminate the 1 byte of space these empty base classes normally consume (alone).
+ - By default `Windows` compilers do not do this optimization. This is to preserve backward binary compatibility.
+ - The Windows compilers allow this optimization to be enabled. Our code uses `EMPTY_BASES_DECL` to enable this optimization. It has to be applied to every structure that has multiple base classes or derives from a such a structure. See `__declspec(empty_bases)`.
+- Packing of the first member of the derived class. In the case where the base class ended with padding:
+ - `gcc` compilers reuse the padding for the first member of the derived class. This effectively removes the padding of the base class in the derived class.
+ - Windows compilers do not remove this padding.
+ - Our code uses the `DAC_ALIGNAS(a)` macro before the first element of the derived class to force the `gcc` compiler to align that member and keep the base classes padding.
+ - The `a` parameter is preferentially the base classes typename.
+ - However, in some cases the compiler will not allow this due to some circular layout issues it causes. In these cases, `a` can refer to a well known type instead. I prefer `int64_t`, `int32_t`, `size_t` ...
+
+#### DacCompareNativeTypes Usage
+
+I wrote and used [DacCompareNativeTypes](https://github.com/dotnet/diagnostics/tree/main/src/tests/DacCompareNativeTypes), to locate and identify type layout issues.
+
+The tool is a bit crude, but it helped get the job done.
+
+The `libcoreclr.so` has a lot of symbols. This proved very slow. So to expedite things, I compared the `dac` and later the `dbi` libraries for structure layout. This had the advantage of eliminating irrelevant data structures.
+
+The compilers generate different debug data and different hidden data structures. The tool tries to overlook these. Be aware that not all differences are real. Some data structures are host only so these are expected to be different.
+
+I usually ran the tool in a debugger so that I could look at other available meta-data the tool keeps. i.e. source file and line number.
+
+### Missing/Different types
+
+There are some cases where types are defined by the Target. These types maybe missing or different on the Host. In these cases we define the cross compilation types in `src/coreclr/inc/crosscomp.h`.
+
+See `T_CRITICAL_SECTION` for a key example. In this case both host and target supported critical sections, but we needed to correctly map the target data structures. So we needed a type defined which was the TARGET's `CRITICAL_SECTION`.
+
+So the Target's definition was made available for the cross compile. Additionally the macro was created to make sure references which required the Target's definition could be separated from ones which might need the host's definition.
+
+There is also some defensive programming to make sure these structures accurate. See `T_CRITICAL_SECTION_VALIDATION_MESSAGE` for one example.
+
+### Out of Process Unwinding
+
+To fully support native stack processing, we needed a Target unwinder. For this `libunwind` was also cross-compiled.
+
+See [CMake cross libunwind](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/CMakeLists.txt#L113)
+
+### DBI
+
+I use the term `DAC` in this document to refer to both the `DAC` and the `DBI` debug interface. Both were actually cross compiled. Be aware.
+
+### Build entry point
+
+The main build systme change is adding the ability to set the Target OS on a Windows build.
+
+- See [build-runtime.cmd changes](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/build-runtime.cmd#L133-L134)
+- See [Subsets.props](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/eng/Subsets.props#L191-L197)
+
+There are also changes to the official build to set these flags package the results and upload to the symbol server.
+
+### Client changes
+
+Various changes were required in the DAC clients to consume the new crossdac. These are really out of the scope of this document.
\ No newline at end of file
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 46092b5528018..fc946ddb03b08 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -80,6 +80,7 @@ The PR that reveals the implementation of the `
+
diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props
new file mode 100644
index 0000000000000..7dfc80d41fd14
--- /dev/null
+++ b/eng/SourceBuild.props
@@ -0,0 +1,73 @@
+
+
+
+ runtime
+
+
+
+
+ ./build.sh
+
+ true
+ false
+
+
+ $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)
+ $(__DistroRid)
+
+
+ <_targetRidPlatformIndex>$(TargetRid.LastIndexOfAny("-"))
+ $(TargetRid.Substring(0, $(_targetRidPlatformIndex)))
+ $(TargetRid.Substring($(_targetRidPlatformIndex)).TrimStart('-'))
+
+ minimal
+
+
+
+
+
+
+
+
+
+
+
+
+ $(InnerBuildArgs) --arch $(TargetRidPlatform)
+ $(InnerBuildArgs) --configuration $(Configuration)
+ $(InnerBuildArgs) --ci
+ $(InnerBuildArgs) --allconfigurations
+ $(InnerBuildArgs) --verbosity $(LogVerbosity)
+ $(InnerBuildArgs) --nodereuse false
+ $(InnerBuildArgs) --warnAsError false
+ $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_LIBUNWIND=TRUE
+ $(InnerBuildArgs) /p:MicrosoftNetFrameworkReferenceAssembliesVersion=1.0.0
+ $(InnerBuildArgs) /p:ContinuousIntegrationBuild=true
+ $(InnerBuildArgs) /p:PackageRid=$(TargetRid)
+ $(InnerBuildArgs) /p:NoPgoOptimize=true
+ $(InnerBuildArgs) /p:KeepNativeSymbols=true
+ $(InnerBuildArgs) /p:RuntimeOS=$(TargetRidWithoutPlatform)
+ $(InnerBuildArgs) /p:PortableBuild=$(SourceBuildPortable)
+ $(InnerBuildArgs) /p:BuildDebPackage=false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml
new file mode 100644
index 0000000000000..c1b6dfbf05381
--- /dev/null
+++ b/eng/SourceBuildPrebuiltBaseline.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/eng/Subsets.props b/eng/Subsets.props
index bf6b25bffc731..105b34254c6c2 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -25,8 +25,8 @@
clr+mono+libs+host+packs
mono+libs+packs
-
- clr+libs+host+packs
+
+ clr+libs+host+packs
@@ -49,18 +49,21 @@
$(DefaultMonoSubsets)mono.wasmruntime+
$(DefaultMonoSubsets)mono.aotcross+
$(DefaultMonoSubsets)mono.runtime+mono.corelib+mono.packages
-
+
libs.native+
- $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.pretest+libs.packages
+ $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.packages
+ $(DefaultLibrariesSubsets)+libs.pretest
- host.native+host.pkg+host.tools+host.tests
+ host.native+host.tools
+ $(DefaultHostSubsets)+host.pkg+host.tests
host.native
packs.product
$(DefaultPacksSubsets)+packs.tests
+ $(DefaultPacksSubsets)+packs.installers
@@ -204,12 +207,12 @@
+ $(CoreClrProjectRoot)tools\r2rtest\R2RTest.csproj" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/>
+
+ Test="true" Category="clr" Condition="'$(__DistroRid)' != 'linux-musl-x64' and '$(DotNetBuildFromSource)' != 'true'"/>
@@ -221,7 +224,7 @@
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 2485b69e1147a..9c44cabfca804 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,6 +9,7 @@
https://github.com/dotnet/arcade
4a2b475948d498b89fedef7cf890883f49bc1ea3
+
https://github.com/dotnet/arcade
@@ -206,13 +207,21 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f
-
+
https://github.com/dotnet/emsdk
- defa37b05c734e025292c5747664e970cd2ac444
+ 617928847d1e11458527b8bbafb5577982291847
https://github.com/dotnet/hotreload-utils
25b814e010cd4796cedfbcce72a274c26928f496
+
+ https://github.com/dotnet/runtime-assets
+ 8d7b898b96cbdb868cac343e938173105287ed9e
+
+
+ https://github.com/dotnet/roslyn-analyzers
+ fcddb771f42866f9521f23f093b1f30e129018bb
+
diff --git a/eng/Versions.props b/eng/Versions.props
index 493ee882e5955..3b887fae24aa2 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -45,9 +45,10 @@
3.8.0
- 6.0.0-preview6.21281.1
+
3.10.0-2.final
3.10.0-2.final
+ 6.0.0-rc1.21320.2
6.0.0-beta.21311.3
6.0.0-beta.21311.3
@@ -110,6 +111,7 @@
4.5.0
6.0.0-preview.6.21314.1
+ 6.0.0-beta.21314.1
6.0.0-beta.21307.1
6.0.0-beta.21307.1
6.0.0-beta.21307.1
@@ -138,7 +140,7 @@
These are used as reference assemblies only, so they must not take a ProdCon/source-build
version. Insert "RefOnly" to avoid assignment via PVP.
-->
- 16.8.0
+ 16.9.0
$(RefOnlyMicrosoftBuildVersion)
$(RefOnlyMicrosoftBuildVersion)
$(RefOnlyMicrosoftBuildVersion)
@@ -174,7 +176,7 @@
11.1.0-alpha.1.21308.1
11.1.0-alpha.1.21308.1
- 6.0.0-preview.6.21275.1
- $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version)
+ 6.0.0-preview.7.21323.1
+ $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version)
diff --git a/eng/packaging.props b/eng/packaging.props
index 87cecef871a69..534d3ddcf321b 100644
--- a/eng/packaging.props
+++ b/eng/packaging.props
@@ -16,10 +16,6 @@
-
- false
-
- true
false
diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml
index daedd73b0e4bf..defd34be84ae2 100644
--- a/eng/pipelines/common/platform-matrix.yml
+++ b/eng/pipelines/common/platform-matrix.yml
@@ -219,7 +219,7 @@ jobs:
targetRid: browser-wasm
platform: Browser_wasm
container:
- image: ubuntu-18.04-webassembly-20210519131124-ba00c14
+ image: ubuntu-18.04-webassembly-20210531091624-f5c7a43
registry: mcr
jobParameters:
runtimeFlavor: ${{ parameters.runtimeFlavor }}
diff --git a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
index c45d9f2f5d37b..f42787af8ebd2 100644
--- a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+++ b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
@@ -26,7 +26,7 @@ parameters:
steps:
- - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} $(buildConfigUpper)
+ - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} /p:RuntimeVariant=${{ parameters.runtimeVariant }} $(buildConfigUpper)
displayName: Build Tests
# Send tests to Helix
@@ -40,6 +40,7 @@ steps:
coreClrRepoRoot: $(Build.SourcesDirectory)/src/coreclr
runtimeFlavorDisplayName: ${{ parameters.runtimeFlavorDisplayName }}
shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+ runtimeVariant: ${{ parameters.runtimeVariant }}
${{ if eq(variables['System.TeamProject'], 'public') }}:
creator: $(Build.DefinitionName)
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index b766f1af96aec..7d33d26edb012 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -189,6 +189,6 @@ jobs:
# WebAssembly windows
- ${{ if eq(parameters.platform, 'Browser_wasm_win') }}:
- - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210519130955-ba00c14
+ - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210531091615-f5c7a43
${{ insert }}: ${{ parameters.jobParameters }}
diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml
index eaaa9e08fe2c5..666641354bf5a 100644
--- a/eng/pipelines/runtime-staging.yml
+++ b/eng/pipelines/runtime-staging.yml
@@ -107,7 +107,9 @@ jobs:
runtimeFlavor: mono
platforms:
- MacCatalyst_x64
- - MacCatalyst_arm64
+ # don't run tests on MacCatalyst_arm64 PRs until we can get significantly more devices
+ - ${{ if eq(variables['isFullMatrix'], true) }}:
+ - MacCatalyst_arm64
variables:
# map dependencies variables to local variables
- name: librariesContainsChange
@@ -257,47 +259,43 @@ jobs:
eq(variables['isFullMatrix'], true))
#
-# Build the whole product using Mono and run libraries tests
+# Build the whole product using Mono for Android and run runtime tests with interpreter
#
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
jobTemplate: /eng/pipelines/common/global-build-job.yml
- helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
buildConfig: Release
runtimeFlavor: mono
platforms:
- - Browser_wasm
+ - Android_x64
variables:
- # map dependencies variables to local variables
- - name: librariesContainsChange
- value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
- - name: monoContainsChange
- value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 60
+ - name: timeoutPerTestCollectionInMinutes
+ value: 180
jobParameters:
testGroup: innerloop
- nameSuffix: AllSubsets_Mono_AOT
- buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true
- timeoutInMinutes: 180
+ nameSuffix: AllSubsets_Mono_RuntimeTests
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 240
+ runtimeVariant: monointerpreter
condition: >-
or(
- eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
- eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
- extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
extraStepsParameters:
creator: dotnet-bot
testRunNamePrefixSuffix: Mono_$(_BuildConfig)
- extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true
- scenarios:
- - normal
- condition: >-
- or(
- eq(variables['librariesContainsChange'], true),
- eq(variables['monoContainsChange'], true),
- eq(variables['isFullMatrix'], true))
-
#
# Build the whole product using Mono for Android and run runtime tests with Android devices
@@ -309,7 +307,7 @@ jobs:
buildConfig: Release
runtimeFlavor: mono
platforms:
- #- Android_arm64 # disabled due to https://github.com/dotnet/runtime/issues/47850
+ - Android_arm64
variables:
- ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
- name: _HelixSource
@@ -331,11 +329,13 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(variables['isFullMatrix'], true))
- # extra steps, run tests
- extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
- extraStepsParameters:
- creator: dotnet-bot
- testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ # don't run tests on PRs until we can get significantly more devices
+ ${{ if eq(variables['isFullMatrix'], true) }}:
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
# Run disabled installer tests on Linux x64
- template: /eng/pipelines/common/platform-matrix.yml
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 148566ea7a262..931a65a05e11e 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -347,6 +347,48 @@ jobs:
eq(variables['monoContainsChange'], true),
eq(variables['isFullMatrix'], true))
+#
+# Build for Browser/wasm with RunAOTCompilation=true
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ # map dependencies variables to local variables
+ - name: librariesContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ - name: monoContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_AOT
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true
+ scenarios:
+ - normal
+ condition: >-
+ or(
+ eq(variables['librariesContainsChange'], true),
+ eq(variables['monoContainsChange'], true),
+ eq(variables['isFullMatrix'], true))
+
# Build and test libraries under single-file publishing
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/resolveContract.targets b/eng/resolveContract.targets
index d98ef8dffb7bd..8c99a9059defb 100644
--- a/eng/resolveContract.targets
+++ b/eng/resolveContract.targets
@@ -25,6 +25,7 @@
diff --git a/eng/restore/harvestPackages.targets b/eng/restore/harvestPackages.targets
index a7fc8aa34655f..07eb5a91a7a8e 100644
--- a/eng/restore/harvestPackages.targets
+++ b/eng/restore/harvestPackages.targets
@@ -1,4 +1,14 @@
+
+
+ $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\
+ $(PackagingTaskAssembly)netcoreapp3.1\
+ $(PackagingTaskAssembly)net472\
+
+ $(PackagingTaskAssembly)net5.0\
+ $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll
+
+
diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets
index ce10194388d55..aa4fb305dbd42 100644
--- a/eng/testing/tests.wasm.targets
+++ b/eng/testing/tests.wasm.targets
@@ -35,12 +35,12 @@
- <_AOTBuildCommand>dotnet msbuild publish/AOTTestProjectForHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog
+ <_AOTBuildCommand>dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog
<_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration)
- <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation)
+ <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) /p:EmccLinkOptimizationFlag='-Oz -Wl%252C-O0 -Wl%252C-lto-O0'
<_AOTBuildCommand>$(_AOTBuildCommand) && cd wasm_build/AppBundle
$(_AOTBuildCommand)
@@ -69,8 +69,7 @@
-
-
+
@@ -96,16 +95,23 @@
<_WasmPropertiesToPass
Include="$(%(_WasmPropertyNames.Identity))"
Name="%(_WasmPropertyNames.Identity)"
- ConditionToUse="%(_WasmPropertyNames.ConditionToUse)" />
+ ConditionToUse__="%(_WasmPropertyNames.ConditionToUse__)" />
<_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" />
<_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(TargetPath)' == ''" />
+
+
+ Items="@(_WasmItemsToPass)"
+ OutputFile="$(BundleDir)publish\ProxyProjectForAOTOnHelix.props" />
@@ -124,17 +130,17 @@
-1
-
-
-
+
+ <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" />
+ <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" />
+ <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" />
-
+
+
+
+
-
<_CopyLocalPaths
Include="@(PublishItemsOutputGroupOutputs)"
diff --git a/src/coreclr/.nuget/coreclr-packages.proj b/src/coreclr/.nuget/coreclr-packages.proj
index cef8381866cf1..1347e06d5ff0a 100644
--- a/src/coreclr/.nuget/coreclr-packages.proj
+++ b/src/coreclr/.nuget/coreclr-packages.proj
@@ -5,11 +5,14 @@
+
+
+
+
-
diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
index 0648aed9763d1..52ab4af9bef74 100644
--- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
+++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
@@ -4,5 +4,11 @@
+
+
+
+
+
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
index bd108893d6c22..0779c2401f01d 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
@@ -25,24 +25,26 @@ private static Attribute[] InternalGetCustomAttributes(PropertyInfo element, Typ
if (!inherit)
return attributes;
+ // if this is an index we need to get the parameter types to help disambiguate
+ Type[] indexParamTypes = GetIndexParameterTypes(element);
+ PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes);
+ if (baseProp == null)
+ return attributes;
+
// create the hashtable that keeps track of inherited types
Dictionary types = new Dictionary(11);
// create an array list to collect all the requested attibutes
List attributeList = new List();
- CopyToArrayList(attributeList, attributes, types);
-
- // if this is an index we need to get the parameter types to help disambiguate
- Type[] indexParamTypes = GetIndexParameterTypes(element);
-
-
- PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes);
- while (baseProp != null)
+ CopyToAttributeList(attributeList, attributes, types);
+ do
{
attributes = GetCustomAttributes(baseProp, type, false);
AddAttributesToList(attributeList, attributes, types);
baseProp = GetParentDefinition(baseProp, indexParamTypes);
}
+ while (baseProp != null);
+
Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count);
attributeList.CopyTo(array, 0);
return array;
@@ -123,27 +125,33 @@ private static Attribute[] InternalGetCustomAttributes(EventInfo element, Type t
// walk up the hierarchy chain
Attribute[] attributes = (Attribute[])element.GetCustomAttributes(type, inherit);
- if (inherit)
+ if (!inherit)
{
- // create the hashtable that keeps track of inherited types
- Dictionary types = new Dictionary(11);
- // create an array list to collect all the requested attibutes
- List attributeList = new List();
- CopyToArrayList(attributeList, attributes, types);
-
- EventInfo? baseEvent = GetParentDefinition(element);
- while (baseEvent != null)
- {
- attributes = GetCustomAttributes(baseEvent, type, false);
- AddAttributesToList(attributeList, attributes, types);
- baseEvent = GetParentDefinition(baseEvent);
- }
- Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count);
- attributeList.CopyTo(array, 0);
- return array;
+ return attributes;
}
- else
+
+ EventInfo? baseEvent = GetParentDefinition(element);
+ if (baseEvent == null)
+ {
return attributes;
+ }
+
+ // create the hashtable that keeps track of inherited types
+ // create an array list to collect all the requested attibutes
+ Dictionary types = new Dictionary(11);
+ List attributeList = new List();
+ CopyToAttributeList(attributeList, attributes, types);
+ do
+ {
+ attributes = GetCustomAttributes(baseEvent, type, false);
+ AddAttributesToList(attributeList, attributes, types);
+ baseEvent = GetParentDefinition(baseEvent);
+ }
+ while (baseEvent != null);
+
+ Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count);
+ attributeList.CopyTo(array, 0);
+ return array;
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
@@ -357,7 +365,7 @@ private static bool InternalParamIsDefined(ParameterInfo param, Type type, bool
#endregion
#region Utility
- private static void CopyToArrayList(List attributeList, Attribute[] attributes, Dictionary types)
+ private static void CopyToAttributeList(List attributeList, Attribute[] attributes, Dictionary types)
{
for (int i = 0; i < attributes.Length; i++)
{
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
index 1b825f0dc952f..cf35de2462752 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
@@ -237,6 +237,10 @@ private static CustomAttributeType InitCustomAttributeType(RuntimeType parameter
private static IList GetCustomAttributes(RuntimeModule module, int tkTarget)
{
CustomAttributeRecord[] records = GetCustomAttributeRecords(module, tkTarget);
+ if (records.Length == 0)
+ {
+ return Array.Empty();
+ }
CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length];
for (int i = 0; i < records.Length; i++)
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
index b7e6a58f1fe4b..f01ae3933d0d9 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
@@ -304,9 +304,11 @@ internal ParameterInfo[] GetIndexParametersNoCopy()
// Now copy over the parameter info's and change their
// owning member info to the current property info.
- ParameterInfo[] propParams = new ParameterInfo[numParams];
+ ParameterInfo[] propParams = numParams != 0 ?
+ new ParameterInfo[numParams] :
+ Array.Empty();
- for (int i = 0; i < numParams; i++)
+ for (int i = 0; i < propParams.Length; i++)
propParams[i] = new RuntimeParameterInfo((RuntimeParameterInfo)methParams![i], this);
m_parameters = propParams;
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
index 688e4f83908c7..e064c36746d51 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
@@ -464,8 +464,15 @@ public static IntPtr CreateAggregatedObject(IntPtr pOuter, T o) where T : not
///
/// Checks if the object is classic COM component.
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern bool IsComObject(object o);
+ public static bool IsComObject(object o)
+ {
+ if (o is null)
+ {
+ throw new ArgumentNullException(nameof(o));
+ }
+
+ return o is __ComObject;
+ }
///
/// Release the COM component and if the reference hits 0 zombie this object.
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index 75aff556de7c3..dd17a2fa74418 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -460,8 +460,17 @@ internal RuntimeMethodHandleInternal GetInterfaceMethodImplementation(RuntimeTyp
return GetInterfaceMethodImplementation(new QCallTypeHandle(ref nativeHandle), new QCallTypeHandle(ref nativeInterfaceHandle), interfaceMethodHandle);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool IsComObject(RuntimeType type, bool isGenericCOM);
+ internal static bool IsComObject(RuntimeType type, bool isGenericCOM)
+ {
+#if FEATURE_COMINTEROP
+ if (isGenericCOM)
+ return type == typeof(__ComObject);
+
+ return RuntimeTypeHandle.CanCastTo(type, (RuntimeType)typeof(__ComObject));
+#else
+ return false;
+#endif
+ }
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsInterface(RuntimeType type);
diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
index f1f61fcc67209..9cd2200710753 100644
--- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
+++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
@@ -130,7 +130,7 @@ extern "C"
return TRUE;
}
-extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
+extern "C" DLLEXPORT void jitStartup(ICorJitHost* host)
{
// crossgen2 doesn't invoke DllMain on Linux/Mac (under PAL), so optionally do initialization work here.
InitializeShim();
@@ -157,7 +157,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
pnjitStartup(g_ourJitHost);
}
-extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit()
+extern "C" DLLEXPORT ICorJitCompiler* getJit()
{
DWORD dwRetVal = 0;
PgetJit pngetJit;
diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
index af0ceac75d269..b2ad7f6754a53 100644
--- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
+++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
@@ -108,7 +108,7 @@ extern "C"
return TRUE;
}
-extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
+extern "C" DLLEXPORT void jitStartup(ICorJitHost* host)
{
SetDefaultPaths();
SetLibName();
@@ -141,7 +141,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
pnjitStartup(g_ourJitHost);
}
-extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit()
+extern "C" DLLEXPORT ICorJitCompiler* getJit()
{
DWORD dwRetVal = 0;
PgetJit pngetJit;
diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
index ac720a16983b0..c6c24afb08933 100644
--- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
+++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
@@ -93,7 +93,7 @@ extern "C"
return TRUE;
}
-extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
+extern "C" DLLEXPORT void jitStartup(ICorJitHost* host)
{
SetDefaultPaths();
SetLibName();
@@ -117,7 +117,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host)
pnjitStartup(g_ourJitHost);
}
-extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit()
+extern "C" DLLEXPORT ICorJitCompiler* getJit()
{
DWORD dwRetVal = 0;
PgetJit pngetJit;
diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake
index 425d3d84d6a13..eeb421cac4c2f 100644
--- a/src/coreclr/clrdefinitions.cmake
+++ b/src/coreclr/clrdefinitions.cmake
@@ -57,12 +57,14 @@ if(CLR_CMAKE_HOST_WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif(CLR_CMAKE_HOST_WIN32)
-add_compile_definitions($<$>>:EnC_SUPPORTED>)
-if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
+if (NOT (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX))
+ add_compile_definitions($<$>>:EnC_SUPPORTED>)
+endif()
+if(CLR_CMAKE_TARGET_ARCH_AMD64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32))
if(CLR_CMAKE_TARGET_WIN32)
add_compile_definitions($<$>>:FEATURE_ENC_SUPPORTED>)
endif(CLR_CMAKE_TARGET_WIN32)
-endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
+endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32))
# Features - please keep them alphabetically sorted
if(CLR_CMAKE_TARGET_WIN32)
diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj
index 8e9cef4aa638c..6d675a2a59e24 100644
--- a/src/coreclr/crossgen-corelib.proj
+++ b/src/coreclr/crossgen-corelib.proj
@@ -96,7 +96,8 @@
$(CrossGenDllCmd) -o:$(CoreLibOutputPath)
$(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll'))
$(CrossGenDllCmd) --targetarch:$(TargetArchitecture)
- $(CrossGenDllCmd) -m:$(MergedMibcPath) --embed-pgo-data
+ @(OptimizationMibcFiles->'-m:$(MergedMibcPath)', ' ')
+ $(CrossGenDllCmd) $(MibcArgs) --embed-pgo-data
$(CrossGenDllCmd) -O
$(CrossGenDllCmd) --verify-type-and-field-layout
$(CrossGenDllCmd) $(CoreLibInputPath)
diff --git a/src/coreclr/debug/ee/funceval.cpp b/src/coreclr/debug/ee/funceval.cpp
index 41acc0cb92b9d..de23343b7979b 100644
--- a/src/coreclr/debug/ee/funceval.cpp
+++ b/src/coreclr/debug/ee/funceval.cpp
@@ -1635,7 +1635,7 @@ static void GCProtectAllPassedArgs(DebuggerEval *pDE,
#endif
}
#endif // TARGET_X86
-
+ FALLTHROUGH;
default:
//
// Ignorable - no need to protect
diff --git a/src/coreclr/debug/ee/i386/x86walker.cpp b/src/coreclr/debug/ee/i386/x86walker.cpp
index ef7dabb3be4c2..cb262e770ecdc 100644
--- a/src/coreclr/debug/ee/i386/x86walker.cpp
+++ b/src/coreclr/debug/ee/i386/x86walker.cpp
@@ -415,7 +415,7 @@ void NativeWalker::DecodeInstructionForPatchSkip(const BYTE *address, Instructio
case 2:
case 3:
pInstrAttrib->m_fIsCall = true;
- // fall through
+ FALLTHROUGH;
case 4:
case 5:
pInstrAttrib->m_fIsAbsBranch = true;
diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp
index 909e5580c72fb..9cb439e650411 100644
--- a/src/coreclr/gc/gc.cpp
+++ b/src/coreclr/gc/gc.cpp
@@ -641,6 +641,10 @@ process_sync_log_stats()
#ifndef DACCESS_COMPILE
uint32_t g_num_active_processors = 0;
+// Note that when a join is no longer used we still keep the values here because
+// tooling already recognized them as having the meaning they were assigned originally.
+// It doesn't break tooling if we stop using them but does if we assign a new meaning
+// to them.
enum gc_join_stage
{
gc_join_init_cpu_mapping = 0,
@@ -681,6 +685,7 @@ enum gc_join_stage
gc_join_after_commit_soh_no_gc = 35,
gc_join_expand_loh_no_gc = 36,
gc_join_final_no_gc = 37,
+ // No longer in use but do not remove, see comments for this enum.
gc_join_disable_software_write_watch = 38,
gc_join_max = 39
};
@@ -10808,7 +10813,7 @@ void gc_heap::return_free_region (heap_segment* region)
heap_segment_mem (region), num_basic_regions, num_free_regions));
for (int i = 0; i < num_basic_regions; i++)
{
- uint8_t* basic_region_start = region_start + (i << min_segment_size_shr);
+ uint8_t* basic_region_start = region_start + ((size_t)i << min_segment_size_shr);
heap_segment* basic_region = get_region_info (basic_region_start);
heap_segment_allocated (basic_region) = 0;
#ifdef MULTIPLE_HEAPS
@@ -20587,6 +20592,7 @@ void gc_heap::garbage_collect (int n)
update_collection_counts_for_no_gc();
#ifdef MULTIPLE_HEAPS
+ gc_start_event.Reset();
gc_t_join.restart();
#endif //MULTIPLE_HEAPS
}
@@ -31804,23 +31810,6 @@ void gc_heap::background_mark_phase ()
//concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
concurrent_print_time_delta ("NRre LOH");
-#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
-#ifdef MULTIPLE_HEAPS
- bgc_t_join.join(this, gc_join_disable_software_write_watch);
- if (bgc_t_join.joined())
-#endif // MULTIPLE_HEAPS
- {
- // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
- // avoid further perf penalty after the runtime is restarted
- SoftwareWriteWatch::DisableForGCHeap();
-
-#ifdef MULTIPLE_HEAPS
- dprintf(3, ("Restarting BGC threads after disabling software write watch"));
- bgc_t_join.restart();
-#endif // MULTIPLE_HEAPS
- }
-#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
-
dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
bgc_overflow_count = 0;
@@ -31845,6 +31834,12 @@ void gc_heap::background_mark_phase ()
if (bgc_t_join.joined())
#endif //MULTIPLE_HEAPS
{
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+ // The runtime is suspended, take this opportunity to pause tracking written pages to
+ // avoid further perf penalty after the runtime is restarted
+ SoftwareWriteWatch::DisableForGCHeap();
+#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+
GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
#ifdef MULTIPLE_HEAPS
@@ -42262,6 +42257,15 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_
newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, gen_num, acontext->alloc_bytes_uoh);
ASSERT(((size_t)newAlloc & 7) == 0);
+#ifdef MULTIPLE_HEAPS
+ if (flags & GC_ALLOC_FINALIZE)
+ {
+ // the heap may have changed due to heap balancing - it's important
+ // to register the object for finalization on the heap it was allocated on
+ hp = gc_heap::heap_of ((uint8_t*)newAlloc);
+ }
+#endif //MULTIPLE_HEAPS
+
#ifdef FEATURE_STRUCTALIGN
newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
#endif // FEATURE_STRUCTALIGN
@@ -42281,6 +42285,16 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_
newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext, flags);
}
+#ifdef MULTIPLE_HEAPS
+ if (flags & GC_ALLOC_FINALIZE)
+ {
+ // the heap may have changed due to heap balancing - it's important
+ // to register the object for finalization on the heap it was allocated on
+ hp = acontext->get_alloc_heap()->pGenGCHeap;
+ assert ((newAlloc == nullptr) || (hp == gc_heap::heap_of ((uint8_t*)newAlloc)));
+ }
+#endif //MULTIPLE_HEAPS
+
#ifdef FEATURE_STRUCTALIGN
newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
#endif // FEATURE_STRUCTALIGN
@@ -44409,6 +44423,9 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
unsigned int Seg = gen_segment (gen);
Object** startIndex = SegQueue (Seg);
+
+ dprintf (3, ("RelocateFinalizationData gen=%d, [%Ix,%Ix[", gen, startIndex, SegQueue (FreeList)));
+
for (Object** po = startIndex; po < SegQueue (FreeList);po++)
{
GCHeap::Relocate (po, &sc);
@@ -44418,6 +44435,8 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
void
CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
{
+ dprintf(3, ("UpdatePromotedGenerations gen=%d, gen_0_empty_p=%d", gen, gen_0_empty_p));
+
// update the generation fill pointers.
// if gen_0_empty is FALSE, test each object to find out if
// it was promoted or not
@@ -44442,6 +44461,8 @@ CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
int new_gen = g_theGCHeap->WhichGeneration (*po);
if (new_gen != i)
{
+ dprintf (3, ("Moving object %Ix->%Ix from gen %d to gen %d", po, *po, i, new_gen));
+
if (new_gen > i)
{
//promotion
@@ -44473,6 +44494,8 @@ CFinalize::GrowArray()
}
memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
+ dprintf (3, ("Grow finalizer array [%Ix,%Ix[ -> [%Ix,%Ix[", m_Array, m_EndArray, newArray, &m_Array[newArraySize]));
+
//adjust the fill pointers
for (int i = 0; i < FreeList; i++)
{
diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp
index 9408a9b121406..d2798c860adee 100644
--- a/src/coreclr/gc/unix/gcenv.unix.cpp
+++ b/src/coreclr/gc/unix/gcenv.unix.cpp
@@ -884,7 +884,7 @@ static size_t GetLogicalProcessorCacheSizeFromOS()
cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL4_CACHE_SIZE));
#endif
-#if defined(TARGET_LINUX) && !defined(HOST_ARM)
+#if defined(TARGET_LINUX) && !defined(HOST_ARM) && !defined(HOST_X86)
if (cacheSize == 0)
{
//
diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt
index 70b0f7396d89b..bfddaee4b700a 100644
--- a/src/coreclr/gcinfo/CMakeLists.txt
+++ b/src/coreclr/gcinfo/CMakeLists.txt
@@ -82,3 +82,7 @@ create_gcinfo_lib(TARGET gcinfo_unix_armel OS unix ARCH armel)
create_gcinfo_lib(TARGET gcinfo_unix_arm OS unix ARCH arm)
create_gcinfo_lib(TARGET gcinfo_win_arm OS win ARCH arm)
create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86)
+
+if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
+ create_gcinfo_lib(TARGET gcinfo_unix_x86 OS unix ARCH x86)
+endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt
index 892203e81aa94..f06c011e5b963 100644
--- a/src/coreclr/jit/CMakeLists.txt
+++ b/src/coreclr/jit/CMakeLists.txt
@@ -516,6 +516,10 @@ target_compile_definitions(clrjit_unix_arm_${ARCH_HOST_NAME} PRIVATE ARM_SOFTFP
create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm DESTINATIONS .)
create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86 DESTINATIONS .)
+if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
+ create_standalone_jit(TARGET clrjit_unix_x86_${ARCH_HOST_NAME} OS unix ARCH x86 DESTINATIONS .)
+endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
+
if (CLR_CMAKE_TARGET_UNIX)
if (NOT ARCH_TARGET_NAME STREQUAL s390x)
install_clr(TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit)
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index f977ddfe6f31c..e99f047c061a5 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7454,7 +7454,7 @@ void CodeGen::genFnProlog()
// Establish the AMD64 frame pointer after the OS-reported prolog.
if (doubleAlignOrFramePointerUsed())
{
- bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC;
+ const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC;
genEstablishFramePointer(compiler->codeGen->genSPtoFPdelta(), reportUnwindData);
}
#endif // TARGET_AMD64
@@ -8147,7 +8147,31 @@ void CodeGen::genFnEpilog(BasicBlock* block)
/* Compute the size in bytes we've pushed/popped */
- if (!doubleAlignOrFramePointerUsed())
+ bool removeEbpFrame = doubleAlignOrFramePointerUsed();
+
+#ifdef TARGET_AMD64
+ // We only remove the EBP frame using the frame pointer (using `lea rsp, [rbp + const]`)
+ // if we reported the frame pointer in the prolog. The Windows x64 unwinding ABI specifically
+ // disallows this `lea` form:
+ //
+ // See https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code
+ //
+ // "When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the
+ // stack. It may not use lea RSP,constant[RSP] instead. This restriction exists so the unwind code has fewer
+ // patterns to recognize when searching for epilogs."
+ //
+ // Otherwise, we must use `add RSP, constant`, as stated. So, we need to use the same condition
+ // as genFnProlog() used in determining whether to report the frame pointer in the unwind data.
+ // This is a subset of the `doubleAlignOrFramePointerUsed()` cases.
+ //
+ if (removeEbpFrame)
+ {
+ const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC;
+ removeEbpFrame = removeEbpFrame && reportUnwindData;
+ }
+#endif // TARGET_AMD64
+
+ if (!removeEbpFrame)
{
// We have an ESP frame */
@@ -8177,6 +8201,15 @@ void CodeGen::genFnEpilog(BasicBlock* block)
genPopCalleeSavedRegisters();
+#ifdef TARGET_AMD64
+ // In the case where we have an RSP frame, and no frame pointer reported in the OS unwind info,
+ // but we do have a pushed frame pointer and established frame chain, we do need to pop RBP.
+ if (doubleAlignOrFramePointerUsed())
+ {
+ inst_RV(INS_pop, REG_EBP, TYP_I_IMPL);
+ }
+#endif // TARGET_AMD64
+
// Extra OSR adjust to get to where RBP was saved by the original frame, and
// restore RBP.
//
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 2d0d108a3349c..6d7f8c7bc6fdc 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -5509,8 +5509,16 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
fgSetRngChkTarget(indexAddr);
}
- // Change `tree` into an indirection and return.
- tree->ChangeOper(GT_IND);
+ if (!tree->TypeIs(TYP_STRUCT))
+ {
+ tree->ChangeOper(GT_IND);
+ }
+ else
+ {
+ DEBUG_DESTROY_NODE(tree);
+ tree = gtNewObjNode(elemStructType, indexAddr);
+ INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
+ }
GenTreeIndir* const indir = tree->AsIndir();
indir->Addr() = indexAddr;
bool canCSE = indir->CanCSE();
@@ -5520,9 +5528,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
indir->SetDoNotCSE();
}
-#ifdef DEBUG
- indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
-#endif // DEBUG
+ INDEBUG(indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
return indir;
}
diff --git a/src/coreclr/md/enc/CMakeLists.txt b/src/coreclr/md/enc/CMakeLists.txt
index 6bd2518d868c9..d4abb371ffbc4 100644
--- a/src/coreclr/md/enc/CMakeLists.txt
+++ b/src/coreclr/md/enc/CMakeLists.txt
@@ -62,10 +62,6 @@ add_library_clr(mdruntimerw-dbi ${MDRUNTIMERW_SOURCES})
set_target_properties(mdruntimerw-dbi PROPERTIES DBI_COMPONENT TRUE)
target_precompile_headers(mdruntimerw-dbi PRIVATE stdafx.h)
-add_library_clr(mdruntimerw_crossgen ${MDRUNTIMERW_SOURCES})
-set_target_properties(mdruntimerw_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
-target_precompile_headers(mdruntimerw_crossgen PRIVATE stdafx.h)
-
add_library_clr(mdruntimerw_ppdb ${MDRUNTIMERW_SOURCES})
target_compile_definitions(mdruntimerw_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB)
target_precompile_headers(mdruntimerw_ppdb PRIVATE stdafx.h)
diff --git a/src/coreclr/md/inc/VerifyLayouts.inc b/src/coreclr/md/inc/VerifyLayouts.inc
index 14e068ecefa3e..2ca6384b86841 100644
--- a/src/coreclr/md/inc/VerifyLayouts.inc
+++ b/src/coreclr/md/inc/VerifyLayouts.inc
@@ -156,6 +156,9 @@ FIELD(CLiteWeightStgdbRW, m_wszFileName, sizeof(void*))
FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFT, 4)
FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFS, 4)
FIELD(CLiteWeightStgdbRW, m_pStgIO, sizeof(void*))
+#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB
+FIELD(CLiteWeightStgdbRW, m_pPdbHeap, sizeof(void*))
+#endif
END_TYPE(CLiteWeightStgdbRW, 8)
USING_ALIAS(CLiteWeightStgdb__CMiniMdRW__, CLiteWeightStgdb)
diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h
index e3206a589ad99..a1cb17724601e 100644
--- a/src/coreclr/md/inc/metamodel.h
+++ b/src/coreclr/md/inc/metamodel.h
@@ -594,6 +594,7 @@ class CMiniMdBase : public IMetaModelCommonRO
protected:
+ DAC_ALIGNAS(8)
CMiniMdSchema m_Schema; // data header.
ULONG m_TblCount; // Tables in this database.
BOOL m_fVerifiedByTrustedSource; // whether the data was verified by a trusted source
diff --git a/src/coreclr/md/runtime/CMakeLists.txt b/src/coreclr/md/runtime/CMakeLists.txt
index 6ff49d3e803e2..3e1fc8eda75c1 100644
--- a/src/coreclr/md/runtime/CMakeLists.txt
+++ b/src/coreclr/md/runtime/CMakeLists.txt
@@ -59,10 +59,6 @@ add_library_clr(mdruntime-dbi ${MDRUNTIME_SOURCES})
set_target_properties(mdruntime-dbi PROPERTIES DBI_COMPONENT TRUE)
target_precompile_headers(mdruntime-dbi PRIVATE stdafx.h)
-add_library_clr(mdruntime_crossgen ${MDRUNTIME_SOURCES})
-set_target_properties(mdruntime_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
-target_precompile_headers(mdruntime_crossgen PRIVATE stdafx.h)
-
add_library_clr(mdruntime_ppdb ${MDRUNTIME_SOURCES})
target_compile_definitions(mdruntime_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB)
target_precompile_headers(mdruntime_ppdb PRIVATE stdafx.h)
diff --git a/src/coreclr/pal/src/misc/sysinfo.cpp b/src/coreclr/pal/src/misc/sysinfo.cpp
index 1a9ca8fbfba72..8f935b3e3ea1a 100644
--- a/src/coreclr/pal/src/misc/sysinfo.cpp
+++ b/src/coreclr/pal/src/misc/sysinfo.cpp
@@ -558,7 +558,7 @@ PAL_GetLogicalProcessorCacheSizeFromOS()
cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL4_CACHE_SIZE));
#endif
-#if defined(TARGET_LINUX) && !defined(HOST_ARM)
+#if defined(TARGET_LINUX) && !defined(HOST_ARM) && !defined(HOST_X86)
if (cacheSize == 0)
{
//
diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py
index d4033201ca04e..ce4332c314547 100755
--- a/src/coreclr/scripts/superpmi.py
+++ b/src/coreclr/scripts/superpmi.py
@@ -630,6 +630,39 @@ def create_unique_directory_name(root_directory, base_name):
return full_path
+def create_unique_file_name(directory, base_name, extension):
+ """ Create a unique file name in the specified directory by joining `base_name` and `extension`.
+ If this name already exists, append ".1", ".2", ".3", etc., to the `base_name`
+ name component until the full file name is not found.
+
+ Args:
+ directory (str) : directory in which a new file will be created
+ base_name (str) : the base name of the new filename to be added
+ extension (str) : the filename extension of the new filename to be added
+
+ Returns:
+ (str) The full absolute path of the new filename.
+ """
+
+ directory = os.path.abspath(directory)
+ if not os.path.isdir(directory):
+ try:
+ os.makedirs(directory)
+ except Exception as exception:
+ logging.critical(exception)
+ raise exception
+
+ full_path = os.path.join(directory, base_name + "." + extension)
+
+ count = 1
+ while os.path.isfile(full_path):
+ new_full_path = os.path.join(directory, base_name + "." + str(count) + "." + extension)
+ count += 1
+ full_path = new_full_path
+
+ return full_path
+
+
def get_files_from_path(path, match_func=lambda path: True):
""" Return all files in a directory tree matching a criteria.
@@ -1517,14 +1550,6 @@ def replay(self):
result = True # Assume success
- # Possible return codes from SuperPMI
- #
- # 0 : success
- # -1 : general fatal error (e.g., failed to initialize, failed to read files)
- # -2 : JIT failed to initialize
- # 1 : there were compilation failures
- # 2 : there were assembly diffs
-
with TempDir() as temp_location:
logging.debug("")
logging.debug("Temp Location: %s", temp_location)
@@ -1596,8 +1621,12 @@ def replay(self):
if return_code == 0:
logging.info("Clean SuperPMI replay")
else:
- files_with_replay_failures.append(mch_file)
result = False
+ # Don't report as replay failure missing data (return code 3).
+ # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be
+ # reported as a replay failure.
+ if return_code != 3:
+ files_with_replay_failures.append(mch_file)
if is_nonzero_length_file(fail_mcl_file):
# Unclean replay. Examine the contents of the fail.mcl file to dig into failures.
@@ -1669,14 +1698,6 @@ def replay_with_asm_diffs(self):
result = True # Assume success
- # Possible return codes from SuperPMI
- #
- # 0 : success
- # -1 : general fatal error (e.g., failed to initialize, failed to read files)
- # -2 : JIT failed to initialize
- # 1 : there were compilation failures
- # 2 : there were assembly diffs
-
# Set up some settings we'll use below.
asm_complus_vars = {
@@ -1744,6 +1765,9 @@ def replay_with_asm_diffs(self):
files_with_asm_diffs = []
files_with_replay_failures = []
+ # List of all Markdown summary files
+ all_md_summary_files = []
+
with TempDir(self.coreclr_args.temp_dir, self.coreclr_args.skip_cleanup) as temp_location:
logging.debug("")
logging.debug("Temp Location: %s", temp_location)
@@ -1804,8 +1828,12 @@ def replay_with_asm_diffs(self):
if return_code == 0:
logging.info("Clean SuperPMI replay")
else:
- files_with_replay_failures.append(mch_file)
result = False
+ # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3).
+ # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be
+ # reported as a replay failure.
+ if return_code != 2 and return_code != 3:
+ files_with_replay_failures.append(mch_file)
artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file)
@@ -1938,7 +1966,10 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
jit_analyze_path = find_file(jit_analyze_file, path_var.split(os.pathsep))
if jit_analyze_path is not None:
# It appears we have a built jit-analyze on the path, so try to run it.
- command = [ jit_analyze_path, "-r", "--base", base_asm_location, "--diff", diff_asm_location ]
+ md_summary_file = os.path.join(asm_root_dir, "summary.md")
+ summary_file_info = ( mch_file, md_summary_file )
+ all_md_summary_files.append(summary_file_info)
+ command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ]
run_and_log(command, logging.INFO)
ran_jit_analyze = True
@@ -1971,8 +2002,32 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
################################################################################################ end of for mch_file in self.mch_files
+ # Report the overall results summary of the asmdiffs run
+
logging.info("Asm diffs summary:")
+ # Construct an overall Markdown summary file.
+
+ if len(all_md_summary_files) > 0:
+ overall_md_summary_file = create_unique_file_name(self.coreclr_args.spmi_location, "diff_summary", "md")
+ if not os.path.isdir(self.coreclr_args.spmi_location):
+ os.makedirs(self.coreclr_args.spmi_location)
+ if os.path.isfile(overall_md_summary_file):
+ os.remove(overall_md_summary_file)
+
+ with open(overall_md_summary_file, "w") as write_fh:
+ for summary_file_info in all_md_summary_files:
+ summary_mch = summary_file_info[0]
+ summary_mch_filename = os.path.basename(summary_mch) # Display just the MCH filename, not the full path
+ summary_file = summary_file_info[1]
+ with open(summary_file, "r") as read_fh:
+ write_fh.write("## " + summary_mch_filename + ":\n\n")
+ shutil.copyfileobj(read_fh, write_fh)
+
+ logging.info(" Summary Markdown file: %s", overall_md_summary_file)
+
+ # Report the set of MCH files with asm diffs and replay failures.
+
if len(files_with_replay_failures) != 0:
logging.info(" Replay failures in %s MCH files:", len(files_with_replay_failures))
for file in files_with_replay_failures:
@@ -3173,7 +3228,7 @@ def setup_spmi_location_arg(spmi_location):
log_file = None
if coreclr_args.log_file is None:
if hasattr(coreclr_args, "spmi_location"):
- log_file = os.path.join(coreclr_args.spmi_location, "superpmi.log")
+ log_file = create_unique_file_name(coreclr_args.spmi_location, "superpmi", "log")
if not os.path.isdir(coreclr_args.spmi_location):
os.makedirs(coreclr_args.spmi_location)
else:
diff --git a/src/coreclr/scripts/superpmi_setup.py b/src/coreclr/scripts/superpmi_setup.py
index e92991cf15ca3..7fd7d62b25600 100644
--- a/src/coreclr/scripts/superpmi_setup.py
+++ b/src/coreclr/scripts/superpmi_setup.py
@@ -59,6 +59,46 @@
is_windows = platform.system() == "Windows"
native_binaries_to_ignore = [
+ "api-ms-win-core-console-l1-1-0.dll",
+ "api-ms-win-core-datetime-l1-1-0.dll",
+ "api-ms-win-core-debug-l1-1-0.dll",
+ "api-ms-win-core-errorhandling-l1-1-0.dll",
+ "api-ms-win-core-file-l1-1-0.dll",
+ "api-ms-win-core-file-l1-2-0.dll",
+ "api-ms-win-core-file-l2-1-0.dll",
+ "api-ms-win-core-handle-l1-1-0.dll",
+ "api-ms-win-core-heap-l1-1-0.dll",
+ "api-ms-win-core-interlocked-l1-1-0.dll",
+ "api-ms-win-core-libraryloader-l1-1-0.dll",
+ "api-ms-win-core-localization-l1-2-0.dll",
+ "api-ms-win-core-memory-l1-1-0.dll",
+ "api-ms-win-core-namedpipe-l1-1-0.dll",
+ "api-ms-win-core-processenvironment-l1-1-0.dll",
+ "api-ms-win-core-processthreads-l1-1-0.dll",
+ "api-ms-win-core-processthreads-l1-1-1.dll",
+ "api-ms-win-core-profile-l1-1-0.dll",
+ "api-ms-win-core-rtlsupport-l1-1-0.dll",
+ "api-ms-win-core-string-l1-1-0.dll",
+ "api-ms-win-core-synch-l1-1-0.dll",
+ "api-ms-win-core-synch-l1-2-0.dll",
+ "api-ms-win-core-sysinfo-l1-1-0.dll",
+ "api-ms-win-core-timezone-l1-1-0.dll",
+ "api-ms-win-core-util-l1-1-0.dll",
+ "api-ms-win-crt-conio-l1-1-0.dll",
+ "api-ms-win-crt-convert-l1-1-0.dll",
+ "api-ms-win-crt-environment-l1-1-0.dll",
+ "api-ms-win-crt-filesystem-l1-1-0.dll",
+ "api-ms-win-crt-heap-l1-1-0.dll",
+ "api-ms-win-crt-locale-l1-1-0.dll",
+ "api-ms-win-crt-math-l1-1-0.dll",
+ "api-ms-win-crt-multibyte-l1-1-0.dll",
+ "api-ms-win-crt-private-l1-1-0.dll",
+ "api-ms-win-crt-process-l1-1-0.dll",
+ "api-ms-win-crt-runtime-l1-1-0.dll",
+ "api-ms-win-crt-stdio-l1-1-0.dll",
+ "api-ms-win-crt-string-l1-1-0.dll",
+ "api-ms-win-crt-time-l1-1-0.dll",
+ "api-ms-win-crt-utility-l1-1-0.dll",
"clretwrc.dll",
"clrgc.dll",
"clrjit.dll",
@@ -68,6 +108,9 @@
"clrjit_unix_arm_x86.dll",
"clrjit_unix_arm64_arm64.dll",
"clrjit_unix_arm64_x64.dll",
+ "clrjit_unix_armel_arm.dll",
+ "clrjit_unix_armel_arm64.dll",
+ "clrjit_unix_armel_x64.dll",
"clrjit_unix_armel_x86.dll",
"clrjit_unix_osx_arm64_arm64.dll",
"clrjit_unix_osx_arm64_x64.dll",
@@ -92,6 +135,7 @@
"CoreShim.dll",
"createdump.exe",
"crossgen.exe",
+ "crossgen2.exe",
"dbgshim.dll",
"ilasm.exe",
"ildasm.exe",
@@ -108,12 +152,15 @@
"mscordbi.dll",
"mscorrc.dll",
"msdia140.dll",
+ "R2RDump.exe",
+ "R2RTest.exe",
"superpmi.exe",
"superpmi-shim-collector.dll",
"superpmi-shim-counter.dll",
"superpmi-shim-simple.dll",
"System.IO.Compression.Native.dll",
"ucrtbase.dll",
+ "xunit.console.exe",
]
MAX_FILES_COUNT = 1500
@@ -202,7 +249,9 @@ def sorter_by_size(pair):
# Credit: https://stackoverflow.com/a/19859907
dirs[:] = [d for d in dirs if d not in exclude_directories]
for name in files:
- if name in exclude_files:
+ # Make the exclude check case-insensitive
+ exclude_files_lower = [filename.lower() for filename in exclude_files]
+ if name.lower() in exclude_files_lower:
continue
curr_file_path = path.join(file_path, name)
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
index 2bf40935a38b6..4109bb701aa9a 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
@@ -782,10 +782,9 @@ public LayoutInt CalculateFieldBaseOffset(MetadataType type, bool requiresAlign8
if (!type.IsValueType && type.HasBaseType)
{
cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned;
- if (!type.BaseType.InstanceByteCountUnaligned.IsIndeterminate)
+ if (!cumulativeInstanceFieldPos.IsIndeterminate)
{
- cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned;
- if (type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout())
+ if (requiresAlignedBase && type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout())
{
cumulativeInstanceFieldPos += LayoutInt.One;
}
diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs
index 84dcc80be1323..33c609ee23b80 100644
--- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs
@@ -23,8 +23,8 @@ public static bool IsBlittableType(TypeDesc type)
&& !baseType.IsWellKnownType(WellKnownType.Object)
&& !baseType.IsWellKnownType(WellKnownType.ValueType);
- // Type is blittable only if parent is also blittable and is not empty.
- if (hasNonTrivialParent && (!IsBlittableType(baseType) || baseType.IsZeroSizedReferenceType))
+ // Type is blittable only if parent is also blittable.
+ if (hasNonTrivialParent && !IsBlittableType(baseType))
{
return false;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
index e54207ff00fb1..74da51246b620 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
@@ -198,11 +198,7 @@ public void EmitPortableExecutable()
if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null)
{
- headerBuilder = PEHeaderProvider.Create(
- imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll,
- dllCharacteristics: default(DllCharacteristics),
- Subsystem.Unknown,
- _nodeFactory.Target);
+ headerBuilder = PEHeaderProvider.Create(Subsystem.Unknown, _nodeFactory.Target);
peIdProvider = new Func, BlobContentId>(content => BlobContentId.FromHash(CryptographicHashProvider.ComputeSourceHash(content)));
timeDateStamp = null;
r2rHeaderExportSymbol = _nodeFactory.Header;
@@ -210,7 +206,7 @@ public void EmitPortableExecutable()
else
{
PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader);
- headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target);
+ headerBuilder = PEHeaderProvider.Create(inputPeReader.PEHeaders.PEHeader.Subsystem, _nodeFactory.Target);
timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp;
r2rHeaderExportSymbol = null;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
index 3f6764b0e804b..c7212368ace07 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
@@ -489,7 +489,7 @@ private void SetPEHeaderTimeStamp(Stream outputStream, int timeDateStamp)
DosHeaderSize +
PESignatureSize +
sizeof(short) + // Machine
- sizeof(short); //NumberOfSections
+ sizeof(short); // NumberOfSections
outputStream.Seek(seekSize, SeekOrigin.Begin);
outputStream.Write(patchedTimestamp, 0, patchedTimestamp.Length);
@@ -664,52 +664,28 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
}
///
- /// Simple helper for filling in PE header information by either copying over
- /// data from a pre-existing input PE header (used for single-assembly R2R files)
- /// or by explicitly specifying the image characteristics (for composite R2R).
+ /// Simple helper for filling in PE header information.
///
static class PEHeaderProvider
{
- ///
- /// Copy PE headers into a PEHeaderBuilder used by PEBuilder.
- ///
- /// Headers to copy
- /// Target architecture to set in the header
- public static PEHeaderBuilder Copy(PEHeaders peHeaders, TargetDetails target)
- {
- return Create(
- peHeaders.CoffHeader.Characteristics,
- peHeaders.PEHeader.DllCharacteristics,
- peHeaders.PEHeader.Subsystem,
- target);
- }
-
///
/// Fill in PE header information into a PEHeaderBuilder used by PEBuilder.
///
- /// Relocs are not present in the PE executable
- /// Extra DLL characteristics to apply
/// Targeting subsystem
/// Target architecture to set in the header
- public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCharacteristics dllCharacteristics, Subsystem subsystem, TargetDetails target)
+ public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target)
{
bool is64BitTarget = target.PointerSize == sizeof(long);
- imageCharacteristics &= ~(Characteristics.Bit32Machine | Characteristics.LargeAddressAware);
- imageCharacteristics |= (is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine);
+ Characteristics imageCharacteristics = Characteristics.ExecutableImage | Characteristics.Dll;
+ imageCharacteristics |= is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine;
- ulong imageBase = PE32HeaderConstants.ImageBase;
- if (target.IsWindows && is64BitTarget && (imageBase <= uint.MaxValue))
- {
- // Base addresses below 4 GiB are reserved for WoW on x64 and disallowed on ARM64.
- // If the input assembly was compiled for anycpu, its base address is 32-bit and we need to fix it.
- imageBase = (imageCharacteristics & Characteristics.Dll) != 0 ? PE64HeaderConstants.DllImageBase : PE64HeaderConstants.ExeImageBase;
- }
+ ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase;
int fileAlignment = 0x200;
if (!target.IsWindows && !is64BitTarget)
{
- // To minimize wasted VA space on 32 bit systems align file to page bounaries (presumed to be 4K).
+ // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K)
fileAlignment = 0x1000;
}
@@ -721,13 +697,11 @@ public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCh
sectionAlignment = fileAlignment;
}
- dllCharacteristics &= DllCharacteristics.AppContainer;
-
- // In Crossgen1, this is under a debug-specific condition 'if (0 == CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NoASLRForNgen))'
- dllCharacteristics |= DllCharacteristics.DynamicBase;
-
// Without NxCompatible the PE executable cannot execute on Windows ARM64
- dllCharacteristics |= DllCharacteristics.NxCompatible | DllCharacteristics.TerminalServerAware;
+ DllCharacteristics dllCharacteristics =
+ DllCharacteristics.DynamicBase |
+ DllCharacteristics.NxCompatible |
+ DllCharacteristics.TerminalServerAware;
if (is64BitTarget)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs
index c3befa87af72d..3523f5d62f8e1 100644
--- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs
+++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs
@@ -74,10 +74,10 @@ public void IsBlittableType_TypeWithBlittableBase_ReturnsTrue(string className)
[InlineData("ClassWithExplicitEmptyBase")]
[InlineData("ClassWithExplicitEmptySizeZeroBase")]
[InlineData("ClassWithSequentialEmptyBase")]
- public void IsBlittableType_TypeWithEmptyBase_ReturnsFalse(string className)
+ public void IsBlittableType_TypeWithEmptyBase_ReturnsTrue(string className)
{
TypeDesc classWithEmptyBase = _testModule.GetType("Marshalling", className);
- Assert.False(MarshalUtils.IsBlittableType(classWithEmptyBase));
+ Assert.True(MarshalUtils.IsBlittableType(classWithEmptyBase));
}
}
}
diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt
index fec22cf9dce38..1ae433adbfd89 100644
--- a/src/coreclr/utilcode/CMakeLists.txt
+++ b/src/coreclr/utilcode/CMakeLists.txt
@@ -76,10 +76,6 @@ set(UTILCODE_DAC_SOURCES
hostimpl.cpp
)
-set(UTILCODE_CROSSGEN_SOURCES
- ${UTILCODE_COMMON_SOURCES}
- hostimpl.cpp
-)
set(UTILCODE_STATICNOHOST_SOURCES
${UTILCODE_COMMON_SOURCES}
@@ -90,7 +86,6 @@ set (UTILCODE_DEPENDENCIES eventing_headers)
convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES})
convert_to_absolute_path(UTILCODE_DAC_SOURCES ${UTILCODE_DAC_SOURCES})
-convert_to_absolute_path(UTILCODE_CROSSGEN_SOURCES ${UTILCODE_CROSSGEN_SOURCES})
convert_to_absolute_path(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_STATICNOHOST_SOURCES})
add_library_clr(utilcode_dac STATIC ${UTILCODE_DAC_SOURCES})
@@ -98,11 +93,9 @@ add_library_clr(utilcode_obj OBJECT ${UTILCODE_SOURCES})
add_library(utilcode INTERFACE)
target_sources(utilcode INTERFACE $)
add_library_clr(utilcodestaticnohost STATIC ${UTILCODE_STATICNOHOST_SOURCES})
-add_library_clr(utilcode_crossgen STATIC ${UTILCODE_CROSSGEN_SOURCES})
if(CLR_CMAKE_HOST_UNIX)
target_link_libraries(utilcodestaticnohost nativeresourcestring)
- target_link_libraries(utilcode_crossgen nativeresourcestring)
target_link_libraries(utilcode_dac nativeresourcestring)
target_link_libraries(utilcode INTERFACE nativeresourcestring)
add_dependencies(utilcode_dac coreclrpal)
@@ -114,20 +107,16 @@ if(CLR_CMAKE_HOST_WIN32)
target_compile_definitions(utilcodestaticnohost PRIVATE _CRTIMP=) # use static version of crt
link_natvis_sources_for_target(utilcodestaticnohost INTERFACE utilcode.natvis)
- link_natvis_sources_for_target(utilcode_crossgen INTERFACE utilcode.natvis)
link_natvis_sources_for_target(utilcode_dac INTERFACE utilcode.natvis)
link_natvis_sources_for_target(utilcode INTERFACE utilcode.natvis)
endif(CLR_CMAKE_HOST_WIN32)
set_target_properties(utilcode_dac PROPERTIES DAC_COMPONENT TRUE)
-set_target_properties(utilcode_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
target_compile_definitions(utilcode_dac PRIVATE SELF_NO_HOST)
target_compile_definitions(utilcodestaticnohost PRIVATE SELF_NO_HOST)
add_dependencies(utilcode_dac ${UTILCODE_DEPENDENCIES})
add_dependencies(utilcode_obj ${UTILCODE_DEPENDENCIES})
-add_dependencies(utilcode_crossgen ${UTILCODE_DEPENDENCIES})
add_dependencies(utilcodestaticnohost ${UTILCODE_DEPENDENCIES})
target_precompile_headers(utilcode_dac PRIVATE [["stdafx.h"]])
target_precompile_headers(utilcode_obj PRIVATE [["stdafx.h"]])
-target_precompile_headers(utilcode_crossgen PRIVATE [["stdafx.h"]])
target_precompile_headers(utilcodestaticnohost PRIVATE [["stdafx.h"]])
diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp
index 3b46c56a41587..639cb02099bf4 100644
--- a/src/coreclr/utilcode/pedecoder.cpp
+++ b/src/coreclr/utilcode/pedecoder.cpp
@@ -1034,14 +1034,29 @@ CHECK PEDecoder::CheckCorHeader() const
IMAGE_COR20_HEADER *pCor = GetCorHeader();
+ // Currently composite r2r images miss some information, for example the version is 0.0.
+ // We may want to change that to something more conforming and explicit.
+ // For now, for compatibility purposes, we will accept that as a valid format.
+ bool possiblyCompositeR2R =
+ pCor->MinorRuntimeVersion == 0 &&
+ pCor->MajorRuntimeVersion == 0;
+
//CHECK(((ULONGLONG)pCor & 0x3)==0);
// If the file is COM+ 1.0, which by definition has nothing the runtime can
// use, or if the file requires a newer version of this engine than us,
// it cannot be run by this engine.
- CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR);
+ if (!possiblyCompositeR2R)
+ CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR);
+#ifdef HOST_WINDOWS
CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK));
+#else
+ CHECK(CheckDirectory(
+ &pCor->MetaData,
+ possiblyCompositeR2R ? 0 : IMAGE_SCN_MEM_WRITE,
+ HasNativeHeader() ? NULL_OK : NULL_NOT_OK));
+#endif
CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK));
CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK));
CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK));
@@ -1083,7 +1098,7 @@ CHECK PEDecoder::CheckCorHeader() const
// IL library files (really a misnomer - these are native images or ReadyToRun images)
// only they can have a native image header
- if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0)
+ if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0 && !possiblyCompositeR2R)
{
CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0);
}
@@ -1769,7 +1784,7 @@ void PEDecoder::LayoutILOnly(void *base, bool enableExecution) const
PAGE_READONLY, &oldProtection))
ThrowLastError();
- // Finally, apply proper protection to copied sections
+ // Finally, apply proper protection to copied sections
for (section = sectionStart; section < sectionEnd; section++)
{
// Add appropriate page protection.
diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h
index 8064adfdfc97f..00cc47e9661d0 100644
--- a/src/coreclr/vm/callingconvention.h
+++ b/src/coreclr/vm/callingconvention.h
@@ -423,11 +423,12 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE
//@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here?
TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing();
CONSISTENCY_CHECK(!fldHnd.IsNull());
- pMT = fldHnd.GetMethodTable();
+ pMT = fldHnd.GetMethodTable();
+ FALLTHROUGH;
}
- case ELEMENT_TYPE_PTR:
- case ELEMENT_TYPE_I:
- case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
case ELEMENT_TYPE_I4:
case ELEMENT_TYPE_U4:
{
diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp
index 171251a9a71c9..349a76039cd46 100644
--- a/src/coreclr/vm/codeman.cpp
+++ b/src/coreclr/vm/codeman.cpp
@@ -1718,8 +1718,8 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName, OUT HINSTANCE* phJit, OUT I
}
#ifdef FEATURE_MERGE_JIT_AND_ENGINE
-EXTERN_C void __stdcall jitStartup(ICorJitHost* host);
-EXTERN_C ICorJitCompiler* __stdcall getJit();
+EXTERN_C void jitStartup(ICorJitHost* host);
+EXTERN_C ICorJitCompiler* getJit();
#endif // FEATURE_MERGE_JIT_AND_ENGINE
BOOL EEJitManager::LoadJIT()
diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h
index 1951003113ec9..f0bade34506c6 100644
--- a/src/coreclr/vm/common.h
+++ b/src/coreclr/vm/common.h
@@ -367,6 +367,7 @@ inline void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord)
#include "pefile.inl"
#include "excep.h"
#include "method.hpp"
+#include "field.h"
#include "callingconvention.h"
#include "frames.h"
#include "qcall.h"
diff --git a/src/coreclr/vm/crossgen/CMakeLists.txt b/src/coreclr/vm/crossgen/CMakeLists.txt
deleted file mode 100644
index fa58514532570..0000000000000
--- a/src/coreclr/vm/crossgen/CMakeLists.txt
+++ /dev/null
@@ -1,283 +0,0 @@
-set(VM_CROSSGEN_SOURCES
- ../appdomain.cpp
- ../array.cpp
- ../assembly.cpp
- ../assemblyloadcontext.cpp
- ../assemblyspec.cpp
- ../baseassemblyspec.cpp
- ../binder.cpp
- ../bundle.cpp
- ../callconvbuilder.cpp
- ../castcache.cpp
- ../ceeload.cpp
- ../ceemain.cpp
- ../class.cpp
- ../classhash.cpp
- ../classlayoutinfo.cpp
- ../clrex.cpp
- ../clsload.cpp
- ../codeman.cpp
- ../codeversion.cpp
- ../comdelegate.cpp
- ../compile.cpp
- ../contractimpl.cpp
- ../coreassemblyspec.cpp
- ../corebindresult.cpp
- ../corelib.cpp
- ../crossgencompile.cpp
- ../custommarshalerinfo.cpp
- ../dataimage.cpp
- ../debuginfostore.cpp
- ../decodemd.cpp
- ../dllimport.cpp
- ../dllimportcallback.cpp
- ../domainfile.cpp
- ../ecall.cpp
- ../eeconfig.cpp
- ../eehash.cpp
- ../eetwain.cpp
- ../excep.cpp
- ../field.cpp
- ../fieldmarshaler.cpp
- ../formattype.cpp
- ../frames.cpp
- ../gcinfodecoder.cpp
- ../genericdict.cpp
- ../generics.cpp
- ../genmeth.cpp
- ../hash.cpp
- ../ilinstrumentation.cpp
- ../ilmarshalers.cpp
- ../ilstubcache.cpp
- ../ilstubresolver.cpp
- ../inlinetracking.cpp
- ../instmethhash.cpp
- ../interoputil.cpp
- ../invokeutil.cpp
- ../jithost.cpp
- ../jitinterface.cpp
- ../loaderallocator.cpp
- ../memberload.cpp
- ../method.cpp
- ../methodimpl.cpp
- ../methodtable.cpp
- ../methodtablebuilder.cpp
- ../mlinfo.cpp
- ../nativeimage.cpp
- ../olevariant.cpp
- ../pefile.cpp
- ../peimage.cpp
- ../peimagelayout.cpp
- ../pendingload.cpp
- ../precode.cpp
- ../sigformat.cpp
- ../siginfo.cpp
- ../simplerwlock.cpp
- ../spinlock.cpp
- ../stackingallocator.cpp
- ../stubcache.cpp
- ../stubgen.cpp
- ../stublink.cpp
- ../tailcallhelp.cpp
- ../typectxt.cpp
- ../typedesc.cpp
- ../typeequivalencehash.cpp
- ../typehandle.cpp
- ../typehash.cpp
- ../typeparse.cpp
- ../typestring.cpp
- ../util.cpp
- ../vars.cpp
- ../versionresilienthashcode.cpp
- ../zapsig.cpp
-)
-
-set(VM_CROSSGEN_HEADERS
- ../appdomain.hpp
- ../appdomain.inl
- ../array.h
- ../assembly.hpp
- ../assemblyloadcontext.h
- ../assemblyspec.hpp
- ../assemblyspecbase.h
- ../baseassemblyspec.h
- ../baseassemblyspec.inl
- ../binder.h
- ../ceeload.h
- ../ceeload.inl
- ../ceemain.h
- ../class.h
- ../class.inl
- ../classhash.h
- ../clrex.h
- ../clsload.hpp
- ../clsload.inl
- ../codeman.h
- ../codeversion.h
- ../comdelegate.h
- ../compile.h
- ../contractimpl.h
- ../corelib.h
- ../custommarshalerinfo.h
- ../dataimage.h
- ../debuginfostore.h
- ../decodemd.h
- ../dllimport.h
- ../dllimportcallback.h
- ../domainfile.h
- ../domainfile.inl
- ../ecall.h
- ../eeconfig.h
- ../eehash.h
- ../eehash.inl
- ../excep.h
- ../field.h
- ../fieldmarshaler.h
- ../genericdict.h
- ../generics.h
- ../hash.h
- ../ilinstrumentation.h
- ../ilmarshalers.h
- ../ilstubcache.h
- ../ilstubresolver.h
- ../inlinetracking.h
- ../instmethhash.h
- ../interoputil.h
- ../invokeutil.h
- ../jithost.h
- ../jitinterface.h
- ../loaderallocator.hpp
- ../loaderallocator.inl
- ../memberload.h
- ../method.hpp
- ../method.inl
- ../methodimpl.h
- ../methodtable.h
- ../methodtable.inl
- ../methodtablebuilder.h
- ../methodtablebuilder.inl
- ../mlinfo.h
- ../olevariant.h
- ../pefile.h
- ../pefile.inl
- ../peimage.h
- ../peimage.inl
- ../peimagelayout.h
- ../peimagelayout.inl
- ../pendingload.h
- ../precode.h
- ../sigformat.h
- ../siginfo.hpp
- ../simplerwlock.hpp
- ../spinlock.h
- ../stackingallocator.h
- ../stubcache.h
- ../stubgen.h
- ../stublink.h
- ../stublink.inl
- ../tailcallhelp.h
- ../typectxt.h
- ../typedesc.h
- ../typedesc.inl
- ../typeequivalencehash.hpp
- ../typehandle.h
- ../typehandle.inl
- ../typehash.h
- ../typeparse.h
- ../typestring.h
- ../util.hpp
- ../vars.hpp
- ../versionresilienthashcode.h
- ../zapsig.h
-)
-
-if(FEATURE_READYTORUN)
- list(APPEND VM_CROSSGEN_SOURCES
- ../readytoruninfo.cpp
- )
- list(APPEND VM_CROSSGEN_HEADERS
- ../readytoruninfo.h
- )
-endif(FEATURE_READYTORUN)
-
-include_directories(BEFORE ..)
-include_directories(${CLR_DIR}/gc)
-include_directories(../${ARCH_SOURCES_DIR})
-
-if(CLR_CMAKE_TARGET_ARCH_AMD64)
- list(APPEND VM_CROSSGEN_SOURCES
- ../${ARCH_SOURCES_DIR}/stublinkeramd64.cpp
- )
- list(APPEND VM_CROSSGEN_HEADERS
- ../${ARCH_SOURCES_DIR}/stublinkeramd64.h
- )
-elseif(CLR_CMAKE_TARGET_ARCH_I386)
- list(APPEND VM_CROSSGEN_SOURCES
- ../${ARCH_SOURCES_DIR}/stublinkerx86.cpp
- ../gcdecode.cpp
- )
- list(APPEND VM_CROSSGEN_HEADERS
- ../${ARCH_SOURCES_DIR}/stublinkerx86.h
- )
-elseif(CLR_CMAKE_TARGET_ARCH_ARM)
- list(APPEND VM_CROSSGEN_SOURCES
- ../${ARCH_SOURCES_DIR}/stubs.cpp
- )
-elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
- list(APPEND VM_CROSSGEN_SOURCES
- ../${ARCH_SOURCES_DIR}/stubs.cpp
- )
-elseif(CLR_CMAKE_TARGET_ARCH_S390X)
- list(APPEND VM_CROSSGEN_SOURCES
- # Not supported as VM target
- )
-else()
- clr_unknown_arch()
-endif()
-
-if (CLR_CMAKE_TARGET_WIN32)
-
- # COM interop scenarios
- list(APPEND VM_CROSSGEN_SOURCES
- ../classcompat.cpp
- ../comtoclrcall.cpp
- ../clrtocomcall.cpp
- ../runtimecallablewrapper.cpp
- )
- list(APPEND VM_CROSSGEN_HEADERS
- ../classcompat.h
- ../clrtocomcall.h
- ../comtoclrcall.h
- ../runtimecallablewrapper.h
- )
-
- list(APPEND VM_CROSSGEN_SOURCES ${VM_CROSSGEN_HEADERS})
-endif (CLR_CMAKE_TARGET_WIN32)
-
-if (CLR_CMAKE_HOST_LINUX)
- list(APPEND VM_CROSSGEN_SOURCES
- ../perfmap.cpp
- ../perfinfo.cpp
- )
-endif (CLR_CMAKE_HOST_LINUX)
-
-add_library_clr(cee_crossgen ${VM_CROSSGEN_SOURCES})
-add_dependencies(cee_crossgen eventing_headers)
-set_target_properties(cee_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
-target_precompile_headers(cee_crossgen PRIVATE [["common.h"]])
-if (MSVC)
- # corelib.cpp does not compile with precompiled header file
- set_source_files_properties(../corelib.cpp PROPERTIES COMPILE_FLAGS "/Y-")
-endif()
-
-add_library_clr(corelib_crossgen ../corelib.cpp)
-add_dependencies(corelib_crossgen eventing_headers)
-target_compile_definitions(corelib_crossgen
- PRIVATE
- EnC_SUPPORTED
- FEATURE_EVENT_TRACE
- FEATURE_MULTICOREJIT
- CROSSGEN_CORELIB)
-
-set_target_properties(corelib_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
-
diff --git a/src/coreclr/vm/crossgencompile.cpp b/src/coreclr/vm/crossgencompile.cpp
index df3555e31a7a9..8622505081b14 100644
--- a/src/coreclr/vm/crossgencompile.cpp
+++ b/src/coreclr/vm/crossgencompile.cpp
@@ -271,6 +271,11 @@ void CrawlFrame::GetExactGenericInstantiations(Instantiation *pClassInst, Instan
UNREACHABLE();
}
+void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref)
+{
+ UNREACHABLE();
+}
+
BOOL Object::SupportsInterface(OBJECTREF pObj, MethodTable* pInterfaceMT)
{
UNREACHABLE();
diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp
index 974556232f62e..c6d1b6d2f1edd 100644
--- a/src/coreclr/vm/ecall.cpp
+++ b/src/coreclr/vm/ecall.cpp
@@ -434,12 +434,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /*
#endif // FEATURE_COMINTEROP
)
{
-#ifdef FEATURE_COMINTEROP
- if (g_pBaseCOMObject == NULL)
- {
- COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM);
- }
-
if (pfSharedOrDynamicFCallImpl)
*pfSharedOrDynamicFCallImpl = TRUE;
@@ -448,9 +442,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /*
// FCComCtor does not need to be in the fcall hashtable since it does not erect frame.
return GetEEFuncEntryPoint(FCComCtor);
-#else
- COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM);
-#endif // FEATURE_COMINTEROP
}
if (!pMD->GetModule()->IsSystem())
@@ -571,9 +562,7 @@ BOOL ECall::IsSharedFCallImpl(PCODE pImpl)
PCODE pNativeCode = pImpl;
return
-#ifdef FEATURE_COMINTEROP
(pNativeCode == GetEEFuncEntryPoint(FCComCtor)) ||
-#endif
(pNativeCode == GetEEFuncEntryPoint(COMDelegate::DelegateConstruct));
}
@@ -619,7 +608,12 @@ BOOL ECall::CheckUnusedECalls(SetSHash& usedIDs)
}
-#if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
+#if !defined(CROSSGEN_COMPILE)
+// This function is a stub implementation for the constructor of a ComImport class.
+// The actual work to implement COM Activation (and built-in COM support checks) is done as part
+// of the implementation of object allocation. As a result, the constructor itself has no extra
+// work to do once the object has been allocated. As a result, we just provide a dummy implementation
+// here since a constructor has to have an implementation.
FCIMPL1(VOID, FCComCtor, LPVOID pV)
{
FCALL_CONTRACT;
@@ -627,7 +621,7 @@ FCIMPL1(VOID, FCComCtor, LPVOID pV)
FCUnique(0x34);
}
FCIMPLEND
-#endif // FEATURE_COMINTEROP && !CROSSGEN_COMPILE
+#endif // !CROSSGEN_COMPILE
diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h
index 387bbc2e9ddf6..9b946ad198222 100644
--- a/src/coreclr/vm/ecall.h
+++ b/src/coreclr/vm/ecall.h
@@ -134,8 +134,6 @@ class ECall
static LPVOID GetQCallImpl(MethodDesc * pMD);
};
-#ifdef FEATURE_COMINTEROP
extern "C" FCDECL1(VOID, FCComCtor, LPVOID pV);
-#endif
#endif // _ECALL_H_
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 48a2d7d322e57..1822422f5961c 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -209,7 +209,6 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("GetNumVirtualsAndStaticVirtuals", RuntimeTypeHandle::GetNumVirtualsAndStaticVirtuals)
QCFuncElement("VerifyInterfaceIsImplemented", RuntimeTypeHandle::VerifyInterfaceIsImplemented)
QCFuncElement("GetInterfaceMethodImplementation", RuntimeTypeHandle::GetInterfaceMethodImplementation)
- FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject)
FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType)
FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface)
FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike)
@@ -768,7 +767,6 @@ FCFuncStart(gInteropMarshalFuncs)
#ifdef FEATURE_COMINTEROP
FCFuncElement("GetHRForException", MarshalNative::GetHRForException)
- FCFuncElement("IsComObject", MarshalNative::IsComObject)
FCFuncElement("GetObjectForIUnknownNative", MarshalNative::GetObjectForIUnknownNative)
FCFuncElement("GetUniqueObjectForIUnknownNative", MarshalNative::GetUniqueObjectForIUnknownNative)
FCFuncElement("GetNativeVariantForObjectNative", MarshalNative::GetNativeVariantForObjectNative)
diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp
index 963df734959f8..7fff234ca85ef 100644
--- a/src/coreclr/vm/exceptionhandling.cpp
+++ b/src/coreclr/vm/exceptionhandling.cpp
@@ -1219,7 +1219,7 @@ lExit: ;
invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME;
#else // USE_GC_INFO_DECODER
hdrInfo gcHdrInfo;
- DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo);
+ DecodeGCHdrInfo(codeInfo.GetGCInfoToken(), 0, &gcHdrInfo);
invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET;
#endif // USE_GC_INFO_DECODER
@@ -4640,7 +4640,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME;
#else // USE_GC_INFO_DECODER
hdrInfo gcHdrInfo;
- DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo);
+ DecodeGCHdrInfo(codeInfo.GetGCInfoToken(), 0, &gcHdrInfo);
invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET;
#endif // USE_GC_INFO_DECODER
diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h
index b330797631de0..ff8e488515fae 100644
--- a/src/coreclr/vm/fcall.h
+++ b/src/coreclr/vm/fcall.h
@@ -1145,6 +1145,7 @@ struct FCSigCheck {
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a1, a2) { HCIMPL_PROLOG(funcname)
#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) { HCIMPL_PROLOG(funcname)
+#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a4, a3) { HCIMPL_PROLOG(funcname)
#define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a5, a4, a3) { HCIMPL_PROLOG(funcname)
@@ -1169,6 +1170,7 @@ struct FCSigCheck {
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname)
#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname)
+#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a4, a3) { HCIMPL_PROLOG(funcname)
#define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a5, a4, a3) { HCIMPL_PROLOG(funcname)
@@ -1194,6 +1196,7 @@ struct FCSigCheck {
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname)
#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname)
+#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a3, a4) { HCIMPL_PROLOG(funcname)
#define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a3, a4, a5) { HCIMPL_PROLOG(funcname)
diff --git a/src/coreclr/vm/field.cpp b/src/coreclr/vm/field.cpp
index 025d9ab8d0b3b..74ff841509817 100644
--- a/src/coreclr/vm/field.cpp
+++ b/src/coreclr/vm/field.cpp
@@ -18,6 +18,25 @@
#include "peimagelayout.inl"
+#ifndef DACCESS_COMPILE
+VOID FieldDesc::SetStaticOBJECTREF(OBJECTREF objRef)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END
+
+ GCPROTECT_BEGIN(objRef);
+ OBJECTREF *pObjRef = (OBJECTREF *)GetCurrentStaticAddress();
+ SetObjectReference(pObjRef, objRef);
+ GCPROTECT_END();
+}
+#endif
+
// called from code:MethodTableBuilder::InitializeFieldDescs#InitCall
VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttrs, BOOL fIsStatic, BOOL fIsRVA, BOOL fIsThreadLocal, LPCSTR pszFieldName)
{
diff --git a/src/coreclr/vm/field.h b/src/coreclr/vm/field.h
index be57bec9d0c60..8fa87edc30f26 100644
--- a/src/coreclr/vm/field.h
+++ b/src/coreclr/vm/field.h
@@ -483,22 +483,7 @@ class FieldDesc
return *(OBJECTREF *)GetCurrentStaticAddress();
}
- VOID SetStaticOBJECTREF(OBJECTREF objRef)
- {
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- INJECT_FAULT(COMPlusThrowOM());
- }
- CONTRACTL_END
-
- GCPROTECT_BEGIN(objRef);
- OBJECTREF *pObjRef = (OBJECTREF *)GetCurrentStaticAddress();
- SetObjectReference(pObjRef, objRef);
- GCPROTECT_END();
- }
+ VOID SetStaticOBJECTREF(OBJECTREF objRef);
void* GetStaticValuePtr()
{
diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp
index 74412a9e06d65..0cecfc624a744 100644
--- a/src/coreclr/vm/gchelpers.cpp
+++ b/src/coreclr/vm/gchelpers.cpp
@@ -930,15 +930,25 @@ OBJECTREF AllocateObject(MethodTable *pMT
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
if (fHandleCom && pMT->IsComObjectType())
{
+ if (!g_pConfig->IsBuiltInCOMSupported())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_COM"));
+ }
+
// Create a instance of __ComObject here is not allowed as we don't know what COM object to create
if (pMT == g_pBaseCOMObject)
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
oref = OBJECTREF_TO_UNCHECKED_OBJECTREF(AllocateComObject_ForManaged(pMT));
}
- else
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
+#else // FEATURE_COMINTEROP
+ if (pMT->IsComObjectType())
+ {
+ COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM);
+ }
#endif // FEATURE_COMINTEROP
+ else
{
GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS;
if (pMT->ContainsPointers())
diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp
index e3797b13d508c..2b6697887aa97 100644
--- a/src/coreclr/vm/interoputil.cpp
+++ b/src/coreclr/vm/interoputil.cpp
@@ -820,25 +820,6 @@ BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT)
}
}
-// Returns TRUE iff the argument represents the "__ComObject" type or
-// any type derived from it (i.e. typelib-imported RCWs).
-BOOL IsComWrapperClass(TypeHandle type)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- MethodTable* pMT = type.GetMethodTable();
- if (pMT == NULL)
- return FALSE;
-
- return pMT->IsComObjectType();
-}
-
// Returns TRUE iff the argument represents the "__ComObject" type.
BOOL IsComObjectClass(TypeHandle type)
{
diff --git a/src/coreclr/vm/interoputil.h b/src/coreclr/vm/interoputil.h
index c7209467b4a79..b99764688dd53 100644
--- a/src/coreclr/vm/interoputil.h
+++ b/src/coreclr/vm/interoputil.h
@@ -83,10 +83,6 @@ ULONG SafeReleasePreemp(IUnknown* pUnk, RCW* pRCW = NULL);
// Determines if a COM object can be cast to the specified type.
BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT);
-// includes Types which hold a "ComObject" class
-// and types which are imported through typelib
-BOOL IsComWrapperClass(TypeHandle type);
-
// includes Type which hold a "__ComObject" class
BOOL IsComObjectClass(TypeHandle type);
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index b7f92254c7083..ea70bf8e0ac24 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -5471,7 +5471,7 @@ NOINLINE static void JIT_ReversePInvokeEnterRare2(ReversePInvokeFrame* frame, vo
// As a result, we specially decorate this method to have the correct calling convention
// and argument ordering for an HCALL, but we don't use the HCALL macros and contracts
// since this method doesn't follow the contracts.
-void F_CALL_CONV HCCALL3(JIT_ReversePInvokeEnterTrackTransitions, ReversePInvokeFrame* frame, CORINFO_METHOD_HANDLE handle, void* secretArg)
+HCIMPL3_RAW(void, JIT_ReversePInvokeEnterTrackTransitions, ReversePInvokeFrame* frame, CORINFO_METHOD_HANDLE handle, void* secretArg)
{
_ASSERTE(frame != NULL && handle != NULL);
@@ -5520,8 +5520,9 @@ void F_CALL_CONV HCCALL3(JIT_ReversePInvokeEnterTrackTransitions, ReversePInvoke
INSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg);
#endif
}
+HCIMPLEND_RAW
-void F_CALL_CONV HCCALL1(JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame)
+HCIMPL1_RAW(void, JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame)
{
_ASSERTE(frame != NULL);
@@ -5552,8 +5553,9 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame)
INSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg);
#endif
}
+HCIMPLEND_RAW
-void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeFrame* frame)
+HCIMPL1_RAW(void, JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeFrame* frame)
{
_ASSERTE(frame != NULL);
_ASSERTE(frame->currentThread == GetThread());
@@ -5574,8 +5576,9 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeF
}
#endif
}
+HCIMPLEND_RAW
-void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExit, ReversePInvokeFrame* frame)
+HCIMPL1_RAW(void, JIT_ReversePInvokeExit, ReversePInvokeFrame* frame)
{
_ASSERTE(frame != NULL);
_ASSERTE(frame->currentThread == GetThread());
@@ -5589,6 +5592,7 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExit, ReversePInvokeFrame* frame)
UNINSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg);
#endif
}
+HCIMPLEND_RAW
//========================================================================
//
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index cb5fe80e0c4de..f4b155a500a0a 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -12526,7 +12526,7 @@ void CEEJitInfo::getEHinfo(
#endif // CROSSGEN_COMPILE
#if defined(CROSSGEN_COMPILE)
-EXTERN_C ICorJitCompiler* __stdcall getJit();
+EXTERN_C ICorJitCompiler* getJit();
#endif // defined(CROSSGEN_COMPILE)
#ifdef FEATURE_INTERPRETER
diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp
index 6d1c38b9d3353..b28f34aa26600 100644
--- a/src/coreclr/vm/marshalnative.cpp
+++ b/src/coreclr/vm/marshalnative.cpp
@@ -946,30 +946,6 @@ FCIMPL0(FC_BOOL_RET, MarshalNative::AreComObjectsAvailableForCleanup)
}
FCIMPLEND
-//====================================================================
-// check if the object is classic COM component
-//====================================================================
-FCIMPL1(FC_BOOL_RET, MarshalNative::IsComObject, Object* objUNSAFE)
-{
- FCALL_CONTRACT;
-
- BOOL retVal = FALSE;
- OBJECTREF obj = (OBJECTREF) objUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
-
- if(!obj)
- COMPlusThrowArgumentNull(W("o"));
-
- MethodTable* pMT = obj->GetMethodTable();
- PREFIX_ASSUME(pMT != NULL);
- retVal = pMT->IsComObjectType();
-
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(retVal);
-}
-FCIMPLEND
-
-
//====================================================================
// free the COM component and zombie this object if the ref count hits 0
// further usage of this Object might throw an exception,
diff --git a/src/coreclr/vm/marshalnative.h b/src/coreclr/vm/marshalnative.h
index 790c7316d32b5..8a3615294ffc6 100644
--- a/src/coreclr/vm/marshalnative.h
+++ b/src/coreclr/vm/marshalnative.h
@@ -103,11 +103,6 @@ class MarshalNative
//====================================================================
static FCDECL2(IUnknown*, CreateAggregatedObjectNative, IUnknown* pOuter, Object* refObjUNSAFE);
- //====================================================================
- // check if the object is classic COM component
- //====================================================================
- static FCDECL1(FC_BOOL_RET, IsComObject, Object* objUNSAFE);
-
//====================================================================
// free the COM component and zombie this object
// further usage of this Object might throw an exception,
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 24dfe4c124015..ad827323c69bc 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2906,6 +2906,13 @@ MethodTableBuilder::EnumerateClassMethods()
IDS_CLASSLOAD_BADSPECIALMETHOD,
tok);
}
+
+ // Check for the presence of virtual static methods
+ if (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs))
+ {
+ bmtProp->fHasVirtualStaticMethods = TRUE;
+ }
+
//
// But first - minimal flags validity checks
//
@@ -2972,11 +2979,7 @@ MethodTableBuilder::EnumerateClassMethods()
}
if(IsMdStatic(dwMemberAttrs))
{
- if (fIsClassInterface)
- {
- bmtProp->fHasVirtualStaticMethods = TRUE;
- }
- else
+ if (!fIsClassInterface)
{
// Static virtual methods are only allowed to exist in interfaces
BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD);
diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp
index 5500da1acc872..8338dbc60960b 100644
--- a/src/coreclr/vm/nativeimage.cpp
+++ b/src/coreclr/vm/nativeimage.cpp
@@ -143,59 +143,69 @@ NativeImage *NativeImage::Open(
NewHolder peLoadedImage;
- EX_TRY
+ BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(fullPath, /*pathIsBundleRelative */ true);
+ if (bundleFileLocation.IsValid())
{
- peLoadedImage = PEImageLayout::LoadNative(fullPath);
+ PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_NoCache, bundleFileLocation);
+ peLoadedImage = pImage->GetLayout(PEImageLayout::LAYOUT_MAPPED, PEImage::LAYOUT_CREATEIFNEEDED);
}
- EX_CATCH
+
+ if (peLoadedImage.IsNull())
{
- SString searchPaths(searchPathsConfig);
- SString::CIterator start = searchPaths.Begin();
- while (start != searchPaths.End())
+ EX_TRY
{
- SString::CIterator end = start;
- if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W))
- {
- end = searchPaths.End();
- }
- fullPath.Set(searchPaths, start, (COUNT_T)(end - start));
-
- if (end != searchPaths.End())
- {
- // Skip path separator character
- ++end;
- }
- start = end;
-
- if (fullPath.GetCount() == 0)
- {
- continue;
- }
-
- fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W);
- fullPath += compositeImageFileName;
-
- EX_TRY
- {
- peLoadedImage = PEImageLayout::LoadNative(fullPath);
- break;
- }
- EX_CATCH
+ peLoadedImage = PEImageLayout::LoadNative(fullPath);
+ }
+ EX_CATCH
+ {
+ SString searchPaths(searchPathsConfig);
+ SString::CIterator start = searchPaths.Begin();
+ while (start != searchPaths.End())
{
+ SString::CIterator end = start;
+ if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W))
+ {
+ end = searchPaths.End();
+ }
+ fullPath.Set(searchPaths, start, (COUNT_T)(end - start));
+
+ if (end != searchPaths.End())
+ {
+ // Skip path separator character
+ ++end;
+ }
+ start = end;
+
+ if (fullPath.GetCount() == 0)
+ {
+ continue;
+ }
+
+ fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W);
+ fullPath += compositeImageFileName;
+
+ EX_TRY
+ {
+ peLoadedImage = PEImageLayout::LoadNative(fullPath);
+ break;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions)
}
- EX_END_CATCH(SwallowAllExceptions)
}
- }
- EX_END_CATCH(SwallowAllExceptions)
+ EX_END_CATCH(SwallowAllExceptions)
- if (peLoadedImage.IsNull())
- {
- // Failed to locate the native composite R2R image
- LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n",
- nativeImageFileName,
- path.GetUnicode(),
- searchPathsConfig != nullptr ? searchPathsConfig : W("
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.GetProperties(System.Collections.Hashtable)
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetAttributes
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetClassName
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetComponentName
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetConverter
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultEvent
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultProperty
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEditor(System.Type)
-
-
- ILLink
- IL2026
- member
- M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEvents
-
diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs
index 3c3ac63548ef8..027fe42f0b08e 100644
--- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs
+++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs
@@ -12,6 +12,7 @@
namespace System.Data.Common
{
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public class DbConnectionStringBuilder : IDictionary, ICustomTypeDescriptor
{
// keyword->value currently listed in the connection string
@@ -387,6 +388,7 @@ internal Attribute[] GetAttributesFromCollection(AttributeCollection collection)
return attributes;
}
+ [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")]
private PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection? propertyDescriptors = _propertyDescriptors;
@@ -412,11 +414,16 @@ private PropertyDescriptorCollection GetProperties()
return propertyDescriptors;
}
+ [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")]
protected virtual void GetProperties(Hashtable propertyDescriptors)
{
long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}", ObjectID);
try
{
+ // Below call is necessary to tell the trimmer that it should mark derived types appropriately.
+ // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor.
+ Type thisType = GetType();
+
// show all strongly typed properties (not already added)
// except ConnectionString iff BrowsableConnectionString
Attribute[]? attributes;
@@ -562,16 +569,28 @@ private PropertyDescriptorCollection GetProperties(Attribute[]? attributes)
return new PropertyDescriptorCollection(filteredPropertiesArray);
}
-// TODO: Enable after System.ComponentModel.TypeConverter is annotated
+ // TODO-NULLABLE: Enable after System.ComponentModel.TypeConverter is annotated
#nullable disable
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")]
string ICustomTypeDescriptor.GetClassName()
{
+ // Below call is necessary to tell the trimmer that it should mark derived types appropriately.
+ // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor.
+ Type thisType = GetType();
return TypeDescriptor.GetClassName(this, true);
}
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")]
string ICustomTypeDescriptor.GetComponentName()
{
+ // Below call is necessary to tell the trimmer that it should mark derived types appropriately.
+ // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor.
+ Type thisType = GetType();
return TypeDescriptor.GetComponentName(this, true);
}
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")]
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
@@ -606,8 +625,13 @@ EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")]
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
+ // Below call is necessary to tell the trimmer that it should mark derived types appropriately.
+ // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor.
+ Type thisType = GetType();
return TypeDescriptor.GetEvents(this, true);
}
[RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")]
diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs
new file mode 100644
index 0000000000000..78ac56e9f751a
--- /dev/null
+++ b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs
@@ -0,0 +1,159 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Data.Common;
+using System.ComponentModel;
+using System.Collections;
+
+namespace DbConnectionStringBuilderTrimmingTests
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ DbConnectionStringBuilder2 dcsb2 = new();
+ ICustomTypeDescriptor td = dcsb2;
+
+ if (td.GetClassName() != "DbConnectionStringBuilderTrimmingTests.DbConnectionStringBuilder2")
+ {
+ throw new Exception("Class name got trimmed");
+ }
+
+ if (td.GetComponentName() != "Test Component Name")
+ {
+ throw new Exception("Component name got trimmed");
+ }
+
+ bool foundAttr = false;
+
+ foreach (Attribute attr in td.GetAttributes())
+ {
+ if (attr.GetType().Name == "TestAttribute")
+ {
+ if (attr.ToString() != "Test Attribute Value")
+ {
+ throw new Exception("Test attribute value differs");
+ }
+
+ if (foundAttr)
+ {
+ throw new Exception("More than one attribute found");
+ }
+
+ foundAttr = true;
+ }
+ }
+
+ if (!foundAttr)
+ {
+ throw new Exception("Attribute not found");
+ }
+
+ bool foundEvent = false;
+ bool foundEventWithDisplayName = false;
+
+ foreach (EventDescriptor ev in td.GetEvents())
+ {
+ if (ev.DisplayName == "TestEvent")
+ {
+ if (foundEvent)
+ {
+ throw new Exception("More than one event TestEvent found.");
+ }
+
+ foundEvent = true;
+ }
+
+ if (ev.DisplayName == "Event With DisplayName")
+ {
+ if (foundEventWithDisplayName)
+ {
+ throw new Exception("More than one event with display name found.");
+ }
+
+ foundEventWithDisplayName = true;
+ }
+ }
+
+ if (!foundEvent)
+ {
+ throw new Exception("Event not found");
+ }
+
+ if (!foundEventWithDisplayName)
+ {
+ throw new Exception("Event with DisplayName not found");
+ }
+
+ bool propertyFound = false;
+ foreach (DictionaryEntry kv in dcsb2.GetProperties2())
+ {
+ PropertyDescriptor val = (PropertyDescriptor)kv.Value;
+ if (val.Name == "TestProperty")
+ {
+ if (propertyFound)
+ {
+ throw new Exception("More than one property TestProperty found.");
+ }
+
+ propertyFound = true;
+ }
+ }
+
+ if (!propertyFound)
+ {
+ throw new Exception("Property not found");
+ }
+
+ return 100;
+ }
+ }
+
+ [Test("Test Attribute Value")]
+ class DbConnectionStringBuilder2 : DbConnectionStringBuilder, IComponent
+ {
+#pragma warning disable CS0067 // The event is never used
+ public event EventHandler Disposed;
+ public event Action TestEvent;
+ [DisplayName("Event With DisplayName")]
+ public event Action TestEvent2;
+#pragma warning restore CS0067
+
+ public string TestProperty { get; set; }
+ public ISite Site { get => new TestSite(); set => throw new NotImplementedException(); }
+ public void Dispose() { }
+
+ public Hashtable GetProperties2()
+ {
+ Hashtable propertyDescriptors = new Hashtable();
+ GetProperties(propertyDescriptors);
+ return propertyDescriptors;
+ }
+ }
+
+ class TestSite : INestedSite
+ {
+ public string FullName => null;
+ public IComponent Component => throw new NotImplementedException();
+ public IContainer Container => throw new NotImplementedException();
+ public bool DesignMode => throw new NotImplementedException();
+ public string Name { get => "Test Component Name"; set => throw new NotImplementedException(); }
+ public object GetService(Type serviceType) => null;
+ }
+
+ class TestAttribute : Attribute
+ {
+ public string Test { get; private set; }
+
+ public TestAttribute(string test)
+ {
+ Test = test;
+ }
+
+ public override string ToString()
+ {
+ return Test;
+ }
+ }
+}
diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj
index da4a46f2ae141..7254c33fc9c7b 100644
--- a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj
+++ b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj
@@ -1,5 +1,10 @@
+
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj
index 14a4d33561620..a093eb35ac7fb 100644
--- a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj
+++ b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj
@@ -2,7 +2,7 @@
true
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461
- $(NoWarn);CA1838
+ $(NoWarn);CA1838;CA1847
diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj
index f66b352c9c186..600f6fc58b993 100644
--- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj
+++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj
@@ -2,6 +2,7 @@
true
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461
+ $(NoWarn);CA1847
diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
index f0c7d8eb136bf..cdd65c22c4431 100644
--- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
+++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
@@ -10,6 +10,9 @@
SR.Process_PlatformNotSupported
+
+ true
+
@@ -234,8 +237,6 @@
Link="Common\Interop\Unix\Interop.GetHostName.cs" />
-
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs
new file mode 100644
index 0000000000000..6f84319b0c096
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading;
+
+namespace System.Diagnostics
+{
+ public partial class Process
+ {
+ private static int s_childrenUsingTerminalCount;
+
+ static partial void ConfigureTerminalForChildProcessesInner(int increment)
+ {
+ Debug.Assert(increment != 0);
+
+ int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment);
+ if (increment > 0)
+ {
+ Debug.Assert(s_processStartLock.IsReadLockHeld);
+
+ // At least one child is using the terminal.
+ Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true);
+ }
+ else
+ {
+ Debug.Assert(s_processStartLock.IsWriteLockHeld);
+
+ if (childrenUsingTerminalRemaining == 0)
+ {
+ // No more children are using the terminal.
+ Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false);
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
index 0894dd9e6f7a0..1b52e4d141ebb 100644
--- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
@@ -19,7 +19,6 @@ public partial class Process : IDisposable
private static volatile bool s_initialized;
private static readonly object s_initializedGate = new object();
private static readonly ReaderWriterLockSlim s_processStartLock = new ReaderWriterLockSlim();
- private static int s_childrenUsingTerminalCount;
///
/// Puts a Process component in state to interact with operating system processes that run in a
@@ -1051,26 +1050,9 @@ private static void OnSigChild(int reapAll)
///
internal static void ConfigureTerminalForChildProcesses(int increment)
{
- Debug.Assert(increment != 0);
-
- int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment);
- if (increment > 0)
- {
- Debug.Assert(s_processStartLock.IsReadLockHeld);
-
- // At least one child is using the terminal.
- Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true);
- }
- else
- {
- Debug.Assert(s_processStartLock.IsWriteLockHeld);
-
- if (childrenUsingTerminalRemaining == 0)
- {
- // No more children are using the terminal.
- Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false);
- }
- }
+ ConfigureTerminalForChildProcessesInner(increment);
}
+
+ static partial void ConfigureTerminalForChildProcessesInner(int increment);
}
}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs
index 53c9c743de856..e272d5fa4af1f 100644
--- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs
@@ -42,7 +42,7 @@ public static unsafe IntPtr FindMainWindow(int processId)
private static bool IsMainWindow(IntPtr handle)
{
- return (Interop.User32.GetWindow(handle, GW_OWNER) == IntPtr.Zero) && Interop.User32.IsWindowVisible(handle);
+ return (Interop.User32.GetWindow(handle, GW_OWNER) == IntPtr.Zero) && Interop.User32.IsWindowVisible(handle) != Interop.BOOL.FALSE;
}
[UnmanagedCallersOnly]
@@ -51,7 +51,7 @@ private static unsafe Interop.BOOL EnumWindowsCallback(IntPtr handle, IntPtr ext
MainWindowFinder* instance = (MainWindowFinder*)extraParameter;
int processId = 0; // Avoid uninitialized variable if the window got closed in the meantime
- Interop.User32.GetWindowThreadProcessId(handle, out processId);
+ Interop.User32.GetWindowThreadProcessId(handle, &processId);
if ((processId == instance->_processId) && IsMainWindow(handle))
{
diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml
index ec106d4867087..d5734aab1a2a9 100644
--- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml
+++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml
@@ -1,18 +1,6 @@
-
- ILLink
- IL2026
- member
- M:System.Drawing.BitmapSelector.SameAssemblyOptIn(System.Reflection.Assembly)
-
-
- ILLink
- IL2026
- member
- M:System.Drawing.BitmapSelector.SatelliteAssemblyOptIn(System.Reflection.Assembly)
-
ILLink
IL2050
@@ -80,4 +68,4 @@
M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef)
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs
index fcec880055ae2..b5daa033aa805 100644
--- a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs
+++ b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs
@@ -1,135 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.IO;
+using System.Reflection;
+
namespace System.Drawing
{
- using System.Diagnostics.CodeAnalysis;
- using System.IO;
- using System.Reflection;
-
///
- /// Provides methods to select from multiple bitmaps depending on a "bitmapSuffix" config setting.
+ /// Provides methods to select bitmaps.
///
internal static class BitmapSelector
{
- ///
- /// Gets the bitmap ID suffix defined in the application configuration, or string.Empty if
- /// the suffix is not specified. Internal for unit tests
- ///
- ///
- /// For performance, the suffix is cached in a static variable so it only has to be read
- /// once per appdomain.
- ///
- private static string? s_suffix;
- internal static string? Suffix
- {
- get
- {
- // NOTE: This value is read from the "SystemDrawingSection" of the ConfigurationManager on
- // the .NET Framework. To avoid pulling in a direct dependency to that assembly, we are not
- // reading the value in this implementation.
- return s_suffix;
- }
- set
- {
- // So unit tests can clear the cached suffix
- s_suffix = value;
- }
- }
-
- ///
- /// Appends the current suffix to . The suffix is appended
- /// before the existing extension (if any). Internal for unit tests.
- ///
- ///
- /// The new path with the suffix included. If there is no suffix defined or there are
- /// invalid characters in the original path, the original path is returned.
- ///
- internal static string AppendSuffix(string filePath)
- {
- try
- {
- return Path.ChangeExtension(filePath, Suffix + Path.GetExtension(filePath));
- }
- catch (ArgumentException)
- { // there are invalid characters in the path
- return filePath;
- }
- }
-
- ///
- /// Returns with the current suffix appended (before the
- /// existing extension) if the resulting file path exists; otherwise the original path is
- /// returned.
- ///
- public static string GetFileName(string originalPath)
- {
- if (string.IsNullOrEmpty(Suffix))
- return originalPath;
-
- string newPath = AppendSuffix(originalPath);
- return File.Exists(newPath) ? newPath : originalPath;
- }
-
- // Calls assembly.GetManifestResourceStream in a try/catch and returns null if not found
- private static Stream? GetResourceStreamHelper(Assembly assembly, Type type, string name)
- {
- Stream? stream = null;
- try
- {
- stream = assembly.GetManifestResourceStream(type, name);
- }
- catch (FileNotFoundException)
- {
- }
- return stream;
- }
-
- [RequiresUnreferencedCode("Calls Assembly.GetType which may be trimmed")]
- private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName)
- {
- return DoesAssemblyHaveCustomAttribute(assembly, assembly.GetType(typeName));
- }
-
- private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, Type? attrType)
- {
- if (attrType != null)
- {
- var attr = assembly.GetCustomAttributes(attrType, false);
- if (attr.Length > 0)
- {
- return true;
- }
- }
- return false;
- }
-
- // internal for unit tests
- internal static bool SatelliteAssemblyOptIn(Assembly assembly)
- {
- // Try 4.5 public attribute type first
- if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSatelliteAssemblyAttribute)))
- {
- return true;
- }
-
- // Also load attribute type by name for dlls compiled against older frameworks
- return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute");
- }
-
- // internal for unit tests
- internal static bool SameAssemblyOptIn(Assembly assembly)
- {
- // Try 4.5 public attribute type first
- if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSameAssemblyAttribute)))
- {
- return true;
- }
-
- // Also load attribute type by name for dlls compiled against older frameworks
- return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute");
- }
-
///
/// Returns a resource stream loaded from the appropriate location according to the current
/// suffix.
@@ -138,57 +19,10 @@ internal static bool SameAssemblyOptIn(Assembly assembly)
/// The type whose namespace is used to scope the manifest resource name
/// The name of the manifest resource being requested
///
- /// The manifest resource stream corresponding to with the
- /// current suffix applied; or if that is not found, the stream corresponding to .
+ /// The manifest resource stream corresponding to .
///
public static Stream? GetResourceStream(Assembly assembly, Type type, string originalName)
{
- if (Suffix != string.Empty)
- {
- try
- {
- // Resource with suffix has highest priority
- if (SameAssemblyOptIn(assembly))
- {
- string newName = AppendSuffix(originalName);
- Stream? stream = GetResourceStreamHelper(assembly, type, newName);
- if (stream != null)
- {
- return stream;
- }
- }
- }
- catch
- {
- // Ignore failures and continue to try other options
- }
-
- try
- {
- // Satellite assembly has second priority, using the original name
- if (SatelliteAssemblyOptIn(assembly))
- {
- AssemblyName assemblyName = assembly.GetName();
- assemblyName.Name += Suffix;
- assemblyName.ProcessorArchitecture = ProcessorArchitecture.None;
- Assembly satellite = Assembly.Load(assemblyName);
- if (satellite != null)
- {
- Stream? stream = GetResourceStreamHelper(satellite, type, originalName);
- if (stream != null)
- {
- return stream;
- }
- }
- }
- }
- catch
- {
- // Ignore failures and continue to try other options
- }
- }
-
- // Otherwise fall back to specified assembly and original name requested
return assembly.GetManifestResourceStream(type, originalName);
}
@@ -199,42 +33,11 @@ internal static bool SameAssemblyOptIn(Assembly assembly)
/// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name
/// The name of the manifest resource being requested
///
- /// The manifest resource stream corresponding to with the
- /// current suffix applied; or if that is not found, the stream corresponding to .
+ /// The manifest resource stream corresponding to .
///
public static Stream? GetResourceStream(Type type, string originalName)
{
return GetResourceStream(type.Module.Assembly, type, originalName);
}
-
- ///
- /// Returns an Icon created from a resource stream loaded from the appropriate location according to the current
- /// suffix.
- ///
- /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name
- /// The name of the manifest resource being requested
- ///
- /// The icon created from a manifest resource stream corresponding to with the
- /// current suffix applied; or if that is not found, the stream corresponding to .
- ///
- public static Icon CreateIcon(Type type, string originalName)
- {
- return new Icon(GetResourceStream(type, originalName)!);
- }
-
- ///
- /// Returns an Bitmap created from a resource stream loaded from the appropriate location according to the current
- /// suffix.
- ///
- /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name
- /// The name of the manifest resource being requested
- ///
- /// The bitmap created from a manifest resource stream corresponding to with the
- /// current suffix applied; or if that is not found, the stream corresponding to .
- ///
- public static Bitmap CreateBitmap(Type type, string originalName)
- {
- return new Bitmap(GetResourceStream(type, originalName)!);
- }
}
}
diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs
index 2cc414e3e184a..aad75a27f3516 100644
--- a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs
+++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs
@@ -28,10 +28,6 @@ public class ToolboxBitmapAttribute : Attribute
private static readonly Size s_largeSize = new Size(32, 32);
private static readonly Size s_smallSize = new Size(16, 16);
- // Used to help cache the last result of BitmapSelector.GetFileName.
- private static string? s_lastOriginalFileName;
- private static string? s_lastUpdatedFileName;
-
public ToolboxBitmapAttribute(string imageFile) : this(GetImageFromFile(imageFile, false), GetImageFromFile(imageFile, true))
{
_imageFile = imageFile;
@@ -168,19 +164,6 @@ public override bool Equals([NotNullWhen(true)] object? value)
return b;
}
- // Cache the last result of BitmapSelector.GetFileName because we commonly load images twice
- // in succession from the same file and we don't need to compute the name twice.
- private static string? GetFileNameFromBitmapSelector(string originalName)
- {
- if (originalName != s_lastOriginalFileName)
- {
- s_lastOriginalFileName = originalName;
- s_lastUpdatedFileName = BitmapSelector.GetFileName(originalName);
- }
-
- return s_lastUpdatedFileName;
- }
-
// Just forwards to Image.FromFile eating any non-critical exceptions that may result.
private static Image? GetImageFromFile(string? imageFile, bool large, bool scaled = true)
{
@@ -189,8 +172,6 @@ public override bool Equals([NotNullWhen(true)] object? value)
{
if (imageFile != null)
{
- imageFile = GetFileNameFromBitmapSelector(imageFile);
-
string? ext = Path.GetExtension(imageFile);
if (ext != null && string.Equals(ext, ".ico", StringComparison.OrdinalIgnoreCase))
{
diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs
index 51892986a5df6..b39b223228834 100644
--- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs
+++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs
@@ -24,7 +24,7 @@ public static DriveInfo[] GetDrives()
private static string NormalizeDriveName(string driveName)
{
- if (driveName.Contains("\0")) // string.Contains(char) is .NetCore2.1+ specific
+ if (driveName.Contains('\0'))
{
throw new ArgumentException(SR.Format(SR.Arg_InvalidDriveChars, driveName), nameof(driveName));
}
diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs
new file mode 100644
index 0000000000000..1cfbd351824de
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs
@@ -0,0 +1,179 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Win32.SafeHandles;
+using System.ComponentModel;
+using System.IO.Pipes;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.ServiceProcess;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.IO.Tests
+{
+ [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept
+ public class UnseekableDeviceFileStreamConnectedConformanceTests : ConnectedStreamConformanceTests
+ {
+ protected override async Task CreateConnectedStreamsAsync()
+ {
+ string pipeName = FileSystemTest.GetNamedPipeServerStreamName();
+ string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}");
+
+ var server = new NamedPipeServerStream(pipeName, PipeDirection.In);
+ var clienStream = new FileStream(File.OpenHandle(pipePath, FileMode.Open, FileAccess.Write, FileShare.None), FileAccess.Write);
+
+ await server.WaitForConnectionAsync();
+
+ var serverStrean = new FileStream(new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true), FileAccess.Read);
+
+ server.SafePipeHandle.SetHandleAsInvalid();
+
+ return (serverStrean, clienStream);
+ }
+
+ protected override Type UnsupportedConcurrentExceptionType => null;
+ protected override bool UsableAfterCanceledReads => false;
+ protected override bool FullyCancelableOperations => false;
+ protected override bool BlocksOnZeroByteReads => OperatingSystem.IsWindows();
+ protected override bool SupportsConcurrentBidirectionalUse => false;
+ }
+
+ [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept
+ public class SeekableDeviceFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests
+ {
+ protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
+ {
+ string filePath = Path.GetFullPath(base.GetTestFilePath(index, memberName, lineNumber));
+ string drive = Path.GetPathRoot(filePath);
+ StringBuilder volumeNameBuffer = new StringBuilder(filePath.Length + 1024);
+
+ // the following method maps drive letter like "C:\" to a DeviceID (a DOS device path)
+ // example: "\\?\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\"
+ if (!GetVolumeNameForVolumeMountPoint(drive, volumeNameBuffer, volumeNameBuffer.Capacity))
+ {
+ throw new Win32Exception(Marshal.GetLastPInvokeError(), "GetVolumeNameForVolumeMountPoint failed");
+ }
+
+ // instead of:
+ // 'C:\Users\x\AppData\Local\Temp\y\z
+ // we want something like:
+ // '\\.\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\Users\x\AppData\Local\Temp\y\z
+ string devicePath = filePath.Replace(drive, volumeNameBuffer.ToString());
+ Assert.StartsWith(@"\\?\", devicePath);
+#if DEBUG
+ // we do want to test \\.\ prefix as well
+ devicePath = devicePath.Replace(@"\\?\", @"\\.\");
+#endif
+
+ return devicePath;
+ }
+
+ [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetVolumeNameForVolumeMountPointW", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
+ private static extern bool GetVolumeNameForVolumeMountPoint(string volumeName, StringBuilder uniqueVolumeName, int uniqueNameBufferCapacity);
+ }
+
+ [PlatformSpecific(TestPlatforms.Windows)] // the test setup is Windows-specifc
+ [Collection("NoParallelTests")] // don't run in parallel, as file sharing logic is not thread-safe
+ [OuterLoop("Requires admin privileges to create a file share")]
+ [ConditionalClass(typeof(UncFilePathFileStreamStandaloneConformanceTests), nameof(CanShareFiles))]
+ public class UncFilePathFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests
+ {
+ public static bool CanShareFiles => _canShareFiles.Value;
+
+ private static Lazy _canShareFiles = new Lazy(() =>
+ {
+ if (!PlatformDetection.IsWindowsAndElevated || PlatformDetection.IsWindowsNanoServer)
+ {
+ return false;
+ }
+
+ // the "Server Service" allows for file sharing. It can be disabled on some of our CI machines.
+ using (ServiceController sharingService = new ServiceController("Server"))
+ {
+ return sharingService.Status == ServiceControllerStatus.Running;
+ }
+ });
+
+ protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
+ {
+ string testDirectoryPath = Path.GetFullPath(TestDirectory);
+ string shareName = new DirectoryInfo(testDirectoryPath).Name;
+ string fileName = GetTestFileName(index, memberName, lineNumber);
+
+ SHARE_INFO_502 shareInfo = default;
+ shareInfo.shi502_netname = shareName;
+ shareInfo.shi502_path = testDirectoryPath;
+ shareInfo.shi502_remark = "folder created to test UNC file paths";
+ shareInfo.shi502_max_uses = -1;
+
+ int infoSize = Marshal.SizeOf(shareInfo);
+ IntPtr infoBuffer = Marshal.AllocCoTaskMem(infoSize);
+
+ try
+ {
+ Marshal.StructureToPtr(shareInfo, infoBuffer, false);
+
+ int shareResult = NetShareAdd(string.Empty, 502, infoBuffer, IntPtr.Zero);
+
+ if (shareResult != 0 && shareResult != 2118) // is a failure that is not a NERR_DuplicateShare
+ {
+ throw new Exception($"Failed to create a file share, NetShareAdd returned {shareResult}");
+ }
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(infoBuffer);
+ }
+
+ // now once the folder has been shared we can use "localhost" to access it:
+ // both type of slashes are valid, so let's test one for Debug and another for other configs
+#if DEBUG
+ return @$"//localhost/{shareName}/{fileName}";
+#else
+ return @$"\\localhost\{shareName}\{fileName}";
+#endif
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ string testDirectoryPath = Path.GetFullPath(TestDirectory);
+ string shareName = new DirectoryInfo(testDirectoryPath).Name;
+
+ try
+ {
+ NetShareDel(string.Empty, shareName, 0);
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SHARE_INFO_502
+ {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string shi502_netname;
+ public uint shi502_type;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string shi502_remark;
+ public int shi502_permissions;
+ public int shi502_max_uses;
+ public int shi502_current_uses;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string shi502_path;
+ public IntPtr shi502_passwd;
+ public int shi502_reserved;
+ public IntPtr shi502_security_descriptor;
+ }
+
+ [DllImport(Interop.Libraries.Netapi32)]
+ public static extern int NetShareAdd([MarshalAs(UnmanagedType.LPWStr)]string servername, int level, IntPtr buf, IntPtr parm_err);
+
+ [DllImport(Interop.Libraries.Netapi32)]
+ public static extern int NetShareDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int reserved);
+ }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
index 2ba15d31b4144..d82346f6f4f10 100644
--- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
+++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
@@ -127,5 +127,24 @@ protected void ReadOnly_FileSystemHelper(Action testAction, string subDi
Assert.Equal(0, AdminHelpers.RunAsSudo($"umount {readOnlyDirectory}"));
}
}
+
+ ///
+ /// Determines whether the file system is case sensitive by creating a file in the specified folder and observing the result.
+ ///
+ ///
+ /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable,
+ /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters
+ /// and then tests for its existence with lower-case letters. This could return invalid results in corner
+ /// cases where, for example, different file systems are mounted with differing sensitivities.
+ ///
+ protected static bool GetIsCaseSensitiveByProbing(string probingDirectory)
+ {
+ string pathWithUpperCase = Path.Combine(probingDirectory, "CASESENSITIVETEST" + Guid.NewGuid().ToString("N"));
+ using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose))
+ {
+ string lowerCased = pathWithUpperCase.ToLowerInvariant();
+ return !File.Exists(lowerCased);
+ }
+ }
}
}
diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
index 628bbb4324aea..04d5762685710 100644
--- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
@@ -25,6 +25,7 @@
+
@@ -37,6 +38,8 @@
+
diff --git a/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs
new file mode 100644
index 0000000000000..c908601862eea
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public class PathInternalTests : FileSystemTest
+ {
+ [Fact]
+ [OuterLoop]
+ public void PathInternalIsCaseSensitiveMatchesProbing()
+ {
+ string probingDirectory = TestDirectory;
+ Assert.Equal(GetIsCaseSensitiveByProbing(probingDirectory), PathInternal.IsCaseSensitive);
+ }
+ }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs
index 8d876f30174cd..45d0547a39191 100644
--- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs
+++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs
@@ -46,7 +46,7 @@ public void ThrowsObjectDisposedExceptionForDisposedHandle()
public void ThrowsNotSupportedExceptionForUnseekableFile()
{
using (var server = new AnonymousPipeServerStream(PipeDirection.Out))
- using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true))
+ using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), ownsHandle: false))
{
Assert.Throws(() => MethodUnderTest(handle, Array.Empty(), 0));
}
diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
index a271109ce2fc6..bf845ce77b57f 100644
--- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -51,6 +51,7 @@
+
@@ -71,6 +72,7 @@
+
@@ -79,6 +81,7 @@
+
@@ -191,6 +194,8 @@
+
diff --git a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs
index 6d2993f1509e4..4e5ac8753d3f7 100644
--- a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs
+++ b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs
@@ -93,6 +93,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs
index d647e4b958798..546501aa4d545 100644
--- a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs
+++ b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs
@@ -97,6 +97,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs
index eed5aec365b3e..cf2e967d32a68 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs
@@ -105,6 +105,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs
index f9dccad029402..15c671d12bee9 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs
@@ -118,6 +118,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs
index 5171e62f8ca56..007acd7655f55 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs
@@ -105,6 +105,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs
index 5c2e575377870..4da264fb5ff10 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs
@@ -115,6 +115,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs
index 973d108fc9377..39245a975d878 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs
@@ -131,6 +131,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs
index 23006a571627b..79d370e93d197 100644
--- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs
+++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs
@@ -115,6 +115,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase)
InstanceAppendAllocateAndResetDriver(testCase);
}
+ [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)]
[Theory]
[MemberData(nameof(TestCases))]
public void InstanceMultiAppendGetCurrentHash(TestCase testCase)
diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs
index 202c3ead180f2..d30e42c558a63 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs
+++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs
@@ -13,7 +13,7 @@ public static unsafe void CheckForAvailableVirtualMemory(ulong nativeSize)
{
Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default;
memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX);
- if (Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus))
+ if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) != Interop.BOOL.FALSE)
{
ulong totalVirtual = memoryStatus.ullTotalVirtual;
if (nativeSize >= totalVirtual)
diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs
new file mode 100644
index 0000000000000..0c038f0af75bf
--- /dev/null
+++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+///
+/// Tests that the System.Linq.Expressions.Expression.Property will correctly preserve both
+/// accessors even if only one is referenced by MethodInfo.
+///
+internal class Program
+{
+ static int Main(string[] args)
+ {
+ int result = ExplicitCreation.Test();
+ if (result != 100)
+ return result;
+
+ result = LambdaCreation.Test();
+
+ return result;
+ }
+
+ class ExplicitCreation
+ {
+ class TestType
+ {
+ public bool _testPropertyValue;
+
+ public bool TestProperty {
+ get => _testPropertyValue;
+ set { _testPropertyValue = value; }
+ }
+ }
+
+ public static int Test()
+ {
+ var obj = new TestType();
+ var param = Expression.Parameter(typeof(TestType));
+ var prop = Expression.Property(param, typeof(TestType).GetMethod("get_TestProperty"));
+ ((PropertyInfo)prop.Member).SetValue(obj, true);
+ if (obj._testPropertyValue != true)
+ return -1;
+
+ return 100;
+ }
+ }
+
+ class LambdaCreation
+ {
+ class TestType
+ {
+ public bool _testPropertyValue;
+
+ public bool TestProperty {
+ get => _testPropertyValue;
+ set { _testPropertyValue = value; }
+ }
+ }
+
+ public static int Test()
+ {
+ var obj = new TestType ();
+ Expression> expression = t => t.TestProperty;
+ var prop = ((MemberExpression)expression.Body);
+ ((PropertyInfo)prop.Member).SetValue(obj, true);
+
+ if (obj._testPropertyValue != true)
+ return -2;
+
+ return 100;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs
index 9e384c292c8a0..9e6d360d9ac7a 100644
--- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs
+++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs
@@ -135,11 +135,6 @@ public sealed class PlatformHandler_HttpClientHandler_Proxy_Test : HttpClientHan
public PlatformHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { }
}
- public sealed class PlatformHandler_SchSendAuxRecordHttpTest : SchSendAuxRecordHttpTest
- {
- public PlatformHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { }
- }
-
public sealed class PlatformHandler_HttpClientHandlerTest : HttpClientHandlerTest
{
public PlatformHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { }
@@ -299,13 +294,6 @@ public sealed class PlatformHandler_HttpClientHandler_Proxy_Http2_Test : HttpCli
public PlatformHandler_HttpClientHandler_Proxy_Http2_Test(ITestOutputHelper output) : base(output) { }
}
- public sealed class PlatformHandler_SchSendAuxRecordHttp_Http2_Test : SchSendAuxRecordHttpTest
- {
- protected override Version UseVersion => HttpVersion20.Value;
-
- public PlatformHandler_SchSendAuxRecordHttp_Http2_Test(ITestOutputHelper output) : base(output) { }
- }
-
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))]
public sealed class PlatformHandler_HttpClientHandler_Http2_Test : HttpClientHandlerTest
{
diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj
index c72ec6b0acc11..4b6596d143222 100644
--- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj
+++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj
@@ -127,8 +127,6 @@
Link="Common\System\Net\Http\RepeatedFlushContent.cs" />
-
new LoopbackServer(new LoopbackServer.Options { UseSsl = true }),
+ 1 => Http11LoopbackServerFactory.Singleton.CreateServer(new LoopbackServer.Options { UseSsl = true }),
2 => Http2LoopbackServer.CreateServer(),
_ => throw new Exception("Unknown HTTP version.")
};
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs
index 73a543ac12a21..7a1ceaf88c792 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs
@@ -23,7 +23,6 @@ public HttpClientHandlerTest_Headers(ITestOutputHelper output) : base(output) {
private sealed class DerivedHttpHeaders : HttpHeaders { }
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_RequestWithSimpleHeader_ResponseReferencesUnmodifiedRequestHeaders()
{
const string HeaderKey = "some-header-123", HeaderValue = "this is the expected header value";
@@ -47,7 +46,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "User-Agent is not supported on Browser")]
public async Task SendAsync_UserAgent_CorrectlyWritten()
{
string userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.18 Safari/537.36";
@@ -71,7 +70,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_LargeHeaders_CorrectlyWritten()
{
if (UseVersion == HttpVersion.Version30)
@@ -107,7 +105,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_DefaultHeaders_CorrectlyWritten()
{
const string Version = "2017-04-17";
@@ -137,7 +134,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
[Theory]
[InlineData("\u05D1\u05F1")]
[InlineData("jp\u30A5")]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)]
public async Task SendAsync_InvalidCharactersInHeader_Throw(string value)
{
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
@@ -169,9 +166,14 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
[InlineData("Accept-CharSet", "text/plain, text/json", false)] // invalid format for header but added with TryAddWithoutValidation
[InlineData("Content-Location", "", false)] // invalid format for header but added with TryAddWithoutValidation
[InlineData("Max-Forwards", "NotAnInteger", false)] // invalid format for header but added with TryAddWithoutValidation
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_SpecialHeaderKeyOrValue_Success(string key, string value, bool parsable)
{
+ if (PlatformDetection.IsBrowser && (key == "Content-Location" || key == "Date" || key == "Accept-CharSet"))
+ {
+ // https://fetch.spec.whatwg.org/#forbidden-header-name
+ return;
+ }
+
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
bool contentHeader = false;
@@ -209,7 +211,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
[Theory]
[InlineData("Content-Security-Policy", 4618)]
[InlineData("RandomCustomHeader", 12345)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetAsync_LargeHeader_Success(string headerName, int headerValueLength)
{
var rand = new Random(42);
@@ -234,7 +235,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetAsync_EmptyResponseHeader_Success()
{
IList headers = new HttpHeaderData[] {
@@ -246,8 +246,12 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
using (HttpClient client = CreateHttpClient())
{
- HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false);
- Assert.Equal(headers.Count, response.Headers.Count());
+ HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false);
+ // browser sends more headers
+ if (PlatformDetection.IsNotBrowser)
+ {
+ Assert.Equal(headers.Count, response.Headers.Count());
+ }
Assert.NotNull(response.Headers.GetValues("x-empty"));
}
},
@@ -262,7 +266,6 @@ await server.AcceptConnectionAsync(async connection =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetAsync_MissingExpires_ReturnNull()
{
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
@@ -283,7 +286,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
[InlineData("Thu, 01 Dec 1994 16:00:00 GMT", true)]
[InlineData("-1", false)]
[InlineData("0", false)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_Expires_Success(string value, bool isValid)
{
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
@@ -322,7 +324,6 @@ public void HeadersAdd_CustomExpires_Success(string value, bool isValid)
[Theory]
[InlineData("Accept-Encoding", "identity,gzip")]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task SendAsync_RequestHeaderInResponse_Success(string name, string value)
{
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
@@ -400,7 +401,7 @@ public async Task SendAsync_GetWithInvalidHostHeader_ThrowsException()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)]
public async Task SendAsync_WithZeroLengthHeaderName_Throws()
{
await LoopbackServerFactory.CreateClientAndServerAsync(
@@ -443,7 +444,7 @@ private static readonly (string Name, Encoding ValueEncoding, string[] Values)[]
};
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]
public async Task SendAsync_CustomRequestEncodingSelector_CanSendNonAsciiHeaderValues()
{
await LoopbackServerFactory.CreateClientAndServerAsync(
@@ -498,7 +499,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]
public async Task SendAsync_CustomResponseEncodingSelector_CanReceiveNonAsciiHeaderValues()
{
await LoopbackServerFactory.CreateClientAndServerAsync(
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs
index c7579cd114bb2..f0e58b32ca345 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs
@@ -17,7 +17,6 @@ public class HttpClientHandlerTest_Http1 : HttpClientHandlerTestBase
public HttpClientHandlerTest_Http1(ITestOutputHelper output) : base(output) { }
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
public async Task SendAsync_HostHeader_First()
{
// RFC 7230 3.2.2. Field Order
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs
index ced259294f8c1..4e2426b958e65 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs
@@ -19,7 +19,6 @@ public abstract class HttpClientHandlerTest_RequestRetry : HttpClientHandlerTest
public HttpClientHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { }
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetAsyncOnNewConnection_RetryOnConnectionClosed_Success()
{
await LoopbackServer.CreateClientAndServerAsync(async url =>
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs
index 6d68042a73316..af3bdd764bc72 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs
@@ -98,6 +98,7 @@ await server.AcceptConnectionAsync(async connection =>
}
[Collection(nameof(HttpClientMiniStress))]
+ [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on Browser")]
public abstract class HttpClientMiniStress : HttpClientHandlerTestBase
{
public HttpClientMiniStress(ITestOutputHelper output) : base(output) { }
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs
index 94d59d89b03ff..451bb20d58fa1 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs
@@ -357,8 +357,6 @@ public async Task GetAsync_CustomException_Asynchronous_ThrowsException()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetStringAsync_Success()
{
string content = Guid.NewGuid().ToString();
@@ -378,8 +376,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetStringAsync_CanBeCanceled_AlreadyCanceledCts()
{
var onClientFinished = new SemaphoreSlim(0, 1);
@@ -404,8 +401,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetStringAsync_CanBeCanceled()
{
var cts = new CancellationTokenSource();
@@ -440,7 +436,7 @@ await server.AcceptConnectionAsync(async connection =>
[InlineData(1, 0)]
[InlineData(1, 1)]
[InlineData(1, 2)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetAsync_ContentCanBeCanceled(int getMode, int cancelMode)
{
// cancelMode:
@@ -530,8 +526,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetByteArrayAsync_Success()
{
string content = Guid.NewGuid().ToString();
@@ -552,8 +546,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetByteArrayAsync_CanBeCanceled_AlreadyCanceledCts()
{
var onClientFinished = new SemaphoreSlim(0, 1);
@@ -578,8 +571,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetByteArrayAsync_CanBeCanceled()
{
var cts = new CancellationTokenSource();
@@ -607,8 +599,6 @@ await server.AcceptConnectionAsync(async connection =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task GetStreamAsync_Success()
{
string content = Guid.NewGuid().ToString();
@@ -632,8 +622,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetStreamAsync_CanBeCanceled_AlreadyCanceledCts()
{
var onClientFinished = new SemaphoreSlim(0, 1);
@@ -658,8 +647,7 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)]
public async Task GetStreamAsync_CanBeCanceled()
{
var cts = new CancellationTokenSource();
@@ -916,8 +904,7 @@ public void Send_SingleThread_Succeeds(HttpCompletionOption completionOption)
[Theory]
[InlineData(HttpCompletionOption.ResponseContentRead)]
[InlineData(HttpCompletionOption.ResponseHeadersRead)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")]
public async Task Send_SingleThread_Loopback_Succeeds(HttpCompletionOption completionOption)
{
string content = "Test content";
@@ -970,7 +957,7 @@ await server.AcceptConnectionAsync(async connection =>
[Fact]
[OuterLoop]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")]
public async Task Send_CancelledRequestContent_Throws()
{
CancellationTokenSource cts = new CancellationTokenSource();
@@ -1017,7 +1004,6 @@ await server.AcceptConnectionAsync(async connection =>
[Fact]
[OuterLoop]
[ActiveIssue("https://github.com/dotnet/runtime/issues/39056")]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task Send_TimeoutRequestContent_Throws()
{
await LoopbackServer.CreateClientAndServerAsync(
@@ -1060,7 +1046,7 @@ await server.AcceptConnectionAsync(async connection =>
[Fact]
[OuterLoop]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")]
public async Task Send_CancelledResponseContent_Throws()
{
string content = "Test content";
@@ -1110,12 +1096,13 @@ await server.AcceptConnectionAsync(async connection =>
[Fact]
[OuterLoop]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
- public void Send_TimeoutResponseContent_Throws()
+ [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")]
+ public async Task Send_TimeoutResponseContent_Throws()
{
const string Content = "Test content";
using var server = new LoopbackServer();
+ await server.ListenAsync();
// Ignore all failures from the server. This includes being disposed of before ever accepting a connection,
// which is possible if the client times out so quickly that it hasn't initiated a connection yet.
@@ -1163,8 +1150,7 @@ public static IEnumerable VersionSelectionMemberData()
[Theory]
[MemberData(nameof(VersionSelectionMemberData))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Version is ignored on Browser")]
public async Task SendAsync_CorrectVersionSelected_LoopbackServer(Version requestVersion, HttpVersionPolicy versionPolicy, Version serverVersion, bool useSsl, object expectedResult)
{
await HttpAgnosticLoopbackServer.CreateClientAndServerAsync(
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs
index 214042a185040..b09b0d2dc1a38 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs
@@ -613,8 +613,6 @@ public void Dispose_DisposedObjectThenAccessMembers_ThrowsObjectDisposedExceptio
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsStringAsync_Buffered_IgnoresCancellationToken()
{
string content = Guid.NewGuid().ToString();
@@ -641,8 +639,6 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts()
{
await LoopbackServer.CreateClientAndServerAsync(
@@ -670,8 +666,6 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled()
{
var cts = new CancellationTokenSource();
@@ -706,8 +700,6 @@ await server.AcceptConnectionAsync(async connection =>
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsByteArrayAsync_Buffered_IgnoresCancellationToken()
{
string content = Guid.NewGuid().ToString();
@@ -735,8 +727,6 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts()
{
await LoopbackServer.CreateClientAndServerAsync(
@@ -764,8 +754,6 @@ await LoopbackServer.CreateClientAndServerAsync(
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled()
{
var cts = new CancellationTokenSource();
@@ -802,8 +790,6 @@ await server.AcceptConnectionAsync(async connection =>
[Theory]
[InlineData(true)]
[InlineData(false)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsStreamAsync_Buffered_IgnoresCancellationToken(bool readStreamAsync)
{
string content = Guid.NewGuid().ToString();
@@ -835,10 +821,13 @@ await LoopbackServer.CreateClientAndServerAsync(
[Theory]
[InlineData(true)]
[InlineData(false)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
public async Task ReadAsStreamAsync_Unbuffered_IgnoresCancellationToken(bool readStreamAsync)
{
+ if(PlatformDetection.IsBrowser && !readStreamAsync)
+ {
+ // syncronous operations are not supported on Browser
+ return;
+ }
string content = Guid.NewGuid().ToString();
await LoopbackServer.CreateClientAndServerAsync(
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs
index 167d5a0504c96..18b7fa9fe47cc 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Net.Http.Headers;
using System.Net.Test.Common;
using System.Threading.Tasks;
@@ -12,7 +13,6 @@
namespace System.Net.Http.Functional.Tests
{
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public class HttpRequestMessageTest : HttpClientHandlerTestBase
{
private readonly Version _expectedRequestMessageVersion = HttpVersion.Version11;
@@ -240,8 +240,12 @@ await LoopbackServer.CreateServerAsync(async (server, uri) =>
Task requestTask = client.SendAsync(request);
await server.AcceptConnectionAsync(async connection =>
{
- List headers = await connection.ReadRequestHeaderAsync();
- Assert.DoesNotContain(headers, line => line.StartsWith("Content-length"));
+ var requestData = await connection.ReadRequestDataAsync().ConfigureAwait(false);
+#if TARGET_BROWSER
+ requestData = await connection.HandleCORSPreFlight(requestData);
+#endif
+
+ Assert.DoesNotContain(requestData.Headers, line => line.Name.StartsWith("Content-length"));
await connection.SendResponseAsync();
await requestTask;
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs
index 0ac38b257277f..4f18763f45838 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs
@@ -170,19 +170,19 @@ public sealed class SocketsHttpHandler_HttpClientHandler_Decompression_Tests : H
public SocketsHttpHandler_HttpClientHandler_Decompression_Tests(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test : HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test
{
public SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test : HttpClientHandler_ClientCertificates_Test
{
public SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Proxy is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test : HttpClientHandler_DefaultProxyCredentials_Test
{
public SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test(ITestOutputHelper output) : base(output) { }
@@ -248,13 +248,13 @@ await server.AcceptConnectionAsync(async connection =>
}
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test : HttpClientHandler_ServerCertificates_Test
{
public SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "ResponseDrainTimeout is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_ResponseDrain_Test : HttpClientHandler_ResponseDrain_Test
{
protected override void SetResponseDrainTimeout(HttpClientHandler handler, TimeSpan time)
@@ -560,13 +560,13 @@ public sealed class SocketsHttpHandler_ResponseStreamTest : ResponseStreamTest
public SocketsHttpHandler_ResponseStreamTest(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)]
public sealed class SocketsHttpHandler_HttpClientHandler_SslProtocols_Test : HttpClientHandler_SslProtocols_Test
{
public SocketsHttpHandler_HttpClientHandler_SslProtocols_Test(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_Proxy_Test : HttpClientHandler_Proxy_Test
{
public SocketsHttpHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { }
@@ -588,8 +588,7 @@ protected static Frame MakeDataFrame(int streamId, byte[] data, bool endStream =
new DataFrame(data, (endStream ? FrameFlags.EndStream : FrameFlags.None), 0, streamId);
}
- // System.Net.Sockets is not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54156", TestPlatforms.Browser)]
public class SocketsHttpHandler_Http1_TrailingHeaders_Test : SocketsHttpHandler_TrailingHeaders_Test
{
public SocketsHttpHandler_Http1_TrailingHeaders_Test(ITestOutputHelper output) : base(output) { }
@@ -609,6 +608,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
getResponseTask,
server.AcceptConnectionSendCustomResponseAndCloseAsync(
"HTTP/1.1 200 OK\r\n" +
+ LoopbackServer.CorsHeaders +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n" +
(includeTrailerHeader ? "Trailer: MyCoolTrailerHeader, Hello\r\n" : "") +
@@ -662,6 +662,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
getResponseTask,
server.AcceptConnectionSendCustomResponseAndCloseAsync(
"HTTP/1.1 200 OK\r\n" +
+ LoopbackServer.CorsHeaders +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Trailer: MyCoolTrailerHeader\r\n" +
@@ -751,6 +752,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url =>
}
}, server => server.AcceptConnectionSendCustomResponseAndCloseAsync(
"HTTP/1.1 200 OK\r\n" +
+ LoopbackServer.CorsHeaders +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n" +
$"Trailer: Set-Cookie, MyCoolTrailerHeader, {name}, Hello\r\n" +
@@ -780,6 +782,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
server.AcceptConnectionSendCustomResponseAndCloseAsync(
"HTTP/1.1 200 OK\r\n" +
"Connection: close\r\n" +
+ LoopbackServer.CorsHeaders +
"Transfer-Encoding: chunked\r\n" +
"Trailer: MyCoolTrailerHeader\r\n" +
"\r\n" +
@@ -982,12 +985,6 @@ public async Task Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved()
}
}
- public sealed class SocketsHttpHandler_SchSendAuxRecordHttpTest : SchSendAuxRecordHttpTest
- {
- public SocketsHttpHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { }
- }
-
- [SkipOnPlatform(TestPlatforms.Browser, "Tests hang with chrome. To be investigated")]
public sealed class SocketsHttpHandler_HttpClientHandlerTest : HttpClientHandlerTest
{
public SocketsHttpHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { }
@@ -1017,19 +1014,19 @@ public sealed class SocketsHttpHandlerTest_RequestRetry : HttpClientHandlerTest_
public SocketsHttpHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")]
public sealed class SocketsHttpHandlerTest_Cookies : HttpClientHandlerTest_Cookies
{
public SocketsHttpHandlerTest_Cookies(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")]
public sealed class SocketsHttpHandlerTest_Cookies_Http11 : HttpClientHandlerTest_Cookies_Http11
{
public SocketsHttpHandlerTest_Cookies_Http11(ITestOutputHelper output) : base(output) { }
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "ConnectTimeout is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test : SocketsHttpHandler_Cancellation_Test
{
public SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test(ITestOutputHelper output) : base(output) { }
@@ -1130,15 +1127,13 @@ public void Expect100ContinueTimeout_SetAfterUse_Throws()
}
}
- // BrowserHttpHandler.set_MaxResponseHeadersLength - Not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "MaxResponseHeadersLength is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test : HttpClientHandler_MaxResponseHeadersLength_Test
{
public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test(ITestOutputHelper output) : base(output) { }
}
- //System.Net.Sockets is not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_Authentication_Test : HttpClientHandler_Authentication_Test
{
public SocketsHttpHandler_HttpClientHandler_Authentication_Test(ITestOutputHelper output) : base(output) { }
@@ -1158,7 +1153,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) =>
{
// We need to use ResponseHeadersRead here, otherwise we will hang trying to buffer the response body.
Task getResponseTask = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
- await server.AcceptConnectionAsync(async connection =>
+ await server.AcceptConnectionAsync(async (LoopbackServer.Connection connection) =>
{
Task> serverTask = connection.ReadRequestHeaderAndSendCustomResponseAsync($"HTTP/1.1 101 Switching Protocols\r\nDate: {DateTimeOffset.UtcNow:R}\r\n\r\n");
@@ -1252,7 +1247,7 @@ await server.AcceptConnectionAsync(async connection =>
Task copyTask = clientStream.CopyToAsync(ms);
string bigString = string.Concat(Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", 1000));
- Task lotsOfDataSent = connection.Socket.SendAsync(Encoding.ASCII.GetBytes(bigString), SocketFlags.None);
+ Task lotsOfDataSent = connection.SendResponseAsync(Encoding.ASCII.GetBytes(bigString));
connection.Socket.Shutdown(SocketShutdown.Send);
await copyTask;
await lotsOfDataSent;
@@ -1270,8 +1265,7 @@ public sealed class SocketsHttpHandler_Connect_Test : HttpClientHandler_Connect_
public SocketsHttpHandler_Connect_Test(ITestOutputHelper output) : base(output) { }
}
- // System.Net.Sockets is not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]
public sealed class SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test : HttpClientHandlerTestBase
{
public SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test(ITestOutputHelper output) : base(output) { }
@@ -1468,7 +1462,7 @@ await Http2LoopbackServerFactory.CreateServerAsync(async (server, url) =>
// Wait a small amount of time before making the second request, to give the first request time to timeout.
await Task.Delay(100);
// Grab reference to underlying socket and stream to make sure they are not disposed and closed.
- (Socket socket, Stream stream) = connection.ResetNetwork();
+ (SocketWrapper socket, Stream stream) = connection.ResetNetwork();
// Make second request and expect it to be served from a different connection.
Task request2 = client.GetStringAsync(url);
@@ -1997,8 +1991,7 @@ await Assert.ThrowsAnyAsync(() =>
}
}
- // System.Net.Sockets is not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Headers.Location are not supported on Browser")]
public sealed class SocketsHttpHandlerTest_LocationHeader
{
private static readonly byte[] s_redirectResponseBefore = Encoding.ASCII.GetBytes(
@@ -2671,8 +2664,7 @@ public async Task ConnectCallback_ReturnsNull_ThrowsHttpRequestException(bool us
private static bool PlatformSupportsUnixDomainSockets => Socket.OSSupportsUnixDomainSockets;
}
- // System.Net.Sockets is not supported on this platform
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]
public sealed class SocketsHttpHandlerTest_ConnectCallback_Http11 : SocketsHttpHandlerTest_ConnectCallback
{
public SocketsHttpHandlerTest_ConnectCallback_Http11(ITestOutputHelper output) : base(output) { }
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs
index 633f562c9491b..821794e9f82d5 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs
@@ -113,14 +113,14 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth,
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")]
public sealed class SocksProxyTest_Http1_Async : SocksProxyTest
{
public SocksProxyTest_Http1_Async(ITestOutputHelper helper) : base(helper) { }
protected override Version UseVersion => HttpVersion.Version11;
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")]
public sealed class SocksProxyTest_Http1_Sync : SocksProxyTest
{
public SocksProxyTest_Http1_Sync(ITestOutputHelper helper) : base(helper) { }
@@ -128,7 +128,7 @@ public SocksProxyTest_Http1_Sync(ITestOutputHelper helper) : base(helper) { }
protected override bool TestAsync => false;
}
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")]
public sealed class SocksProxyTest_Http2 : SocksProxyTest
{
public SocksProxyTest_Http2(ITestOutputHelper helper) : base(helper) { }
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
index 7c9b9d6d15738..82246f634e51c 100644
--- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
+++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj
@@ -13,12 +13,17 @@
WasmTestOnBrowser
$(TestArchiveRoot)browseronly/
$(TestArchiveTestsRoot)$(OSPlatformConfig)/
+ $(DefineConstants);TARGET_BROWSER
+
+
+
@@ -169,8 +174,6 @@
Link="Common\System\Net\Http\RepeatedFlushContent.cs" />
-
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs
index 5914cf200c65b..4483920196ada 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs
@@ -119,18 +119,19 @@ private MsQuicApi(NativeApi* vtable)
internal static bool IsQuicSupported { get; }
+ private const int MsQuicVersion = 1;
+
static MsQuicApi()
{
- // TODO: Consider updating all of these delegates to instead use function pointers.
if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle))
{
try
{
- if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpen", out IntPtr msQuicOpenAddress))
+ if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
{
- MsQuicOpenDelegate msQuicOpen =
- Marshal.GetDelegateForFunctionPointer(msQuicOpenAddress);
- uint status = msQuicOpen(out NativeApi* vtable);
+ delegate* unmanaged[Cdecl] msQuicOpenVersion =
+ (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress;
+ uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable);
if (MsQuicStatusHelper.SuccessfulStatusCode(status))
{
IsQuicSupported = true;
@@ -148,6 +149,7 @@ static MsQuicApi()
}
}
+ // TODO: Consider updating all of these delegates to instead use function pointers.
internal RegistrationOpenDelegate RegistrationOpenDelegate { get; }
internal RegistrationCloseDelegate RegistrationCloseDelegate { get; }
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs
index 3700edbb30395..325ee8170aedc 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs
@@ -51,10 +51,6 @@ internal struct NativeApi
internal IntPtr DatagramSend;
}
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- internal delegate uint MsQuicOpenDelegate(
- out NativeApi* registration);
-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate uint SetContextDelegate(
SafeHandle handle,
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs
index 2813f860318e4..e796496bcf1f5 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs
@@ -74,7 +74,6 @@ private sealed class State
internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags)
{
_state.Handle = streamHandle;
- _state.ConnectionState = connectionState;
_canRead = true;
_canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL);
_started = true;
@@ -99,6 +98,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHa
throw new ObjectDisposedException(nameof(QuicConnection));
}
+ _state.ConnectionState = connectionState;
+
if (NetEventSource.Log.IsEnabled())
{
NetEventSource.Info(
@@ -113,10 +114,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F
{
Debug.Assert(connectionState.Handle != null);
- _state.ConnectionState = connectionState;
_canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL);
_canWrite = true;
-
_stateHandle = GCHandle.Alloc(_state);
try
{
@@ -146,6 +145,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F
throw new ObjectDisposedException(nameof(QuicConnection));
}
+ _state.ConnectionState = connectionState;
+
if (NetEventSource.Log.IsEnabled())
{
NetEventSource.Info(
@@ -512,8 +513,17 @@ internal override void Shutdown()
internal override int Read(Span buffer)
{
ThrowIfDisposed();
-
- return ReadAsync(buffer.ToArray()).AsTask().GetAwaiter().GetResult();
+ byte[] rentedBuffer = ArrayPool.Shared.Rent(buffer.Length);
+ try
+ {
+ int readLength = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask().GetAwaiter().GetResult();
+ rentedBuffer.AsSpan(0, readLength).CopyTo(buffer);
+ return readLength;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rentedBuffer);
+ }
}
internal override void Write(ReadOnlySpan buffer)
@@ -569,7 +579,6 @@ private void Dispose(bool disposing)
Marshal.FreeHGlobal(_state.SendQuicBuffers);
if (_stateHandle.IsAllocated) _stateHandle.Free();
CleanupSendState(_state);
- Debug.Assert(_state.ConnectionState != null);
_state.ConnectionState?.RemoveStream(this);
if (NetEventSource.Log.IsEnabled())
diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
index 612da52b26172..277265a17a53a 100644
--- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
+++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
@@ -116,7 +116,6 @@ public async Task ConnectWithCertificateChain()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/52048")]
public async Task WaitForAvailableUnidirectionStreamsAsyncWorks()
{
using QuicListener listener = CreateQuicListener(maxUnidirectionalStreams: 1);
@@ -126,7 +125,7 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks()
using QuicConnection serverConnection = await listener.AcceptConnectionAsync();
await clientTask;
- // No stream openned yet, should return immediately.
+ // No stream opened yet, should return immediately.
Assert.True(clientConnection.WaitForAvailableUnidirectionalStreamsAsync().IsCompletedSuccessfully);
// Open one stream, should wait till it closes.
@@ -141,7 +140,6 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/52048")]
public async Task WaitForAvailableBidirectionStreamsAsyncWorks()
{
using QuicListener listener = CreateQuicListener(maxBidirectionalStreams: 1);
@@ -151,7 +149,7 @@ public async Task WaitForAvailableBidirectionStreamsAsyncWorks()
using QuicConnection serverConnection = await listener.AcceptConnectionAsync();
await clientTask;
- // No stream openned yet, should return immediately.
+ // No stream opened yet, should return immediately.
Assert.True(clientConnection.WaitForAvailableBidirectionalStreamsAsync().IsCompletedSuccessfully);
// Open one stream, should wait till it closes.
diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs
index 6c3670bbc30bb..cd83662a1baa9 100644
--- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs
+++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs
@@ -6,6 +6,7 @@
using System.IO.Tests;
using System.Net.Quic.Implementations;
using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Xunit;
@@ -20,6 +21,8 @@ public sealed class MockQuicStreamConformanceTests : QuicStreamConformanceTests
public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTests
{
protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic;
+ protected override bool UsableAfterCanceledReads => false;
+ protected override bool BlocksOnZeroByteReads => true;
// TODO: These are all hanging, likely due to Stream close behavior.
[ActiveIssue("https://github.com/dotnet/runtime/issues/756")]
@@ -37,59 +40,38 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest
// TODO: new additions, find out the actual reason for hanging
[ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadTimeout_Expires_Throws() => base.ReadTimeout_Expires_Throws();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ConcurrentBidirectionalReadsWrites_Success() => base.ConcurrentBidirectionalReadsWrites_Success();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ArgumentValidation_ThrowsExpectedException() => base.ArgumentValidation_ThrowsExpectedException();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
public override Task ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException() => base.ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException();
[ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode);
[ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads() => base.ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task Disposed_ThrowsObjectDisposedException() => base.Disposed_ThrowsObjectDisposedException();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task Timeout_Roundtrips() => base.Timeout_Roundtrips();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ZeroByteWrite_OtherDataReceivedSuccessfully(ReadWriteMode mode) => base.ZeroByteWrite_OtherDataReceivedSuccessfully(mode);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(flowExecutionContext, continueOnCapturedContext);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadAsync_CancelPendingTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingTask_ThrowsCancellationException();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(flowExecutionContext, continueOnCapturedContext);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadWriteByte_Success() => base.ReadWriteByte_Success();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadWrite_Success(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success(mode, writeSize, startWithFlush);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadWrite_Success_Large(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success_Large(mode, writeSize, startWithFlush);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task Flush_ValidOnWriteableStreamWithNoData_Success() => base.Flush_ValidOnWriteableStreamWithNoData_Success();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadAsync_CancelPendingValueTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingValueTask_ThrowsCancellationException();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported();
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task ReadWrite_CustomMemoryManager_Success(bool useAsync) => base.ReadWrite_CustomMemoryManager_Success(useAsync);
- [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")]
- public override Task Flush_ValidOnReadableStream_Success() => base.Flush_ValidOnReadableStream_Success();
-
}
public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests
{
+ public X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate();
+
+ public bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
+ {
+ Assert.Equal(ServerCertificate.GetCertHash(), certificate?.GetCertHash());
+ return true;
+ }
+
public SslServerAuthenticationOptions GetSslServerAuthenticationOptions()
{
return new SslServerAuthenticationOptions()
{
ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") },
- // TODO: use a cert. MsQuic currently only allows certs that are trusted.
- ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate()
+ ServerCertificate = ServerCertificate
+ };
+ }
+
+ public SslClientAuthenticationOptions GetSslClientAuthenticationOptions()
+ {
+ return new SslClientAuthenticationOptions()
+ {
+ ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") },
+ RemoteCertificateValidationCallback = RemoteCertificateValidationCallback
};
}
@@ -98,30 +80,32 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions()
protected override async Task CreateConnectedStreamsAsync()
{
QuicImplementationProvider provider = Provider;
- var protocol = new SslApplicationProtocol("quictest");
-
var listener = new QuicListener(
provider,
new IPEndPoint(IPAddress.Loopback, 0),
GetSslServerAuthenticationOptions());
+ byte[] buffer = new byte[1] { 42 };
QuicConnection connection1 = null, connection2 = null;
QuicStream stream1 = null, stream2 = null;
-
await WhenAllOrAnyFailed(
Task.Run(async () =>
{
connection1 = await listener.AcceptConnectionAsync();
stream1 = await connection1.AcceptStreamAsync();
+ Assert.Equal(1, await stream1.ReadAsync(buffer));
}),
Task.Run(async () =>
{
connection2 = new QuicConnection(
provider,
listener.ListenEndPoint,
- new SslClientAuthenticationOptions() { ApplicationProtocols = new List() { protocol } });
+ GetSslClientAuthenticationOptions());
await connection2.ConnectAsync();
stream2 = connection2.OpenBidirectionalStream();
+ // OpenBidirectionalStream only allocates ID. We will force stream opening
+ // by Writing there and receiving data on the other side.
+ await stream2.WriteAsync(buffer);
}));
var result = new StreamPairWithOtherDisposables(stream1, stream2);
diff --git a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs
index 63af2603db476..f71c6a9054478 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs
@@ -13,7 +13,7 @@ internal sealed partial class NetEventSource : EventSource
/// The buffer to be logged.
/// The calling member.
[NonEvent]
- public static void DumpBuffer(object thisOrContextObject, ReadOnlyMemory buffer, [CallerMemberName] string? memberName = null)
+ public static void DumpBuffer(object thisOrContextObject, ReadOnlySpan buffer, [CallerMemberName] string? memberName = null)
{
if (Log.IsEnabled())
{
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
index 5d234b5886215..2cbc44c585739 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
@@ -879,7 +879,7 @@ is non-zero.
--*/
internal SecurityStatusPal Encrypt(ReadOnlyMemory buffer, ref byte[] output, out int resultSize)
{
- if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer);
+ if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer.Span);
byte[] writeBuffer = output;
@@ -903,24 +903,12 @@ internal SecurityStatusPal Encrypt(ReadOnlyMemory buffer, ref byte[] outpu
return secStatus;
}
- internal SecurityStatusPal Decrypt(byte[]? payload, ref int offset, ref int count)
+ internal SecurityStatusPal Decrypt(Span buffer, out int outputOffset, out int outputCount)
{
- if ((uint)offset > (uint)(payload == null ? 0 : payload.Length))
- {
- Debug.Fail("Argument 'offset' out of range.");
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
-
- if ((uint)count > (uint)(payload == null ? 0 : payload.Length - offset))
- {
- Debug.Fail("Argument 'count' out of range.");
- throw new ArgumentOutOfRangeException(nameof(count));
- }
-
- SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, payload!, ref offset, ref count);
+ SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, buffer, out outputOffset, out outputCount);
if (NetEventSource.Log.IsEnabled() && status.ErrorCode == SecurityStatusPalErrorCode.OK)
{
- NetEventSource.DumpBuffer(this, payload!, offset, count);
+ NetEventSource.DumpBuffer(this, buffer.Slice(outputOffset, outputCount));
}
return status;
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
index 81f2c92880b55..17858bb0e68b6 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
@@ -43,6 +43,7 @@ private enum Framing
private const int ReadBufferSize = 4096 * 4 + FrameOverhead; // We read in 16K chunks + headers.
private const int InitialHandshakeBufferSize = 4096 + FrameOverhead; // try to fit at least 4K ServerCertificate
private ArrayBuffer _handshakeBuffer;
+ private bool _receivedEOF;
// Used by Telemetry to ensure we log connection close exactly once
// 0 = no handshake
@@ -183,17 +184,6 @@ private SecurityStatusPal EncryptData(ReadOnlyMemory buffer, ref byte[] ou
}
}
- private SecurityStatusPal DecryptData()
- {
- ThrowIfExceptionalOrNotAuthenticated();
- return PrivateDecryptData(_internalBuffer, ref _decryptedBytesOffset, ref _decryptedBytesCount);
- }
-
- private SecurityStatusPal PrivateDecryptData(byte[]? buffer, ref int offset, ref int count)
- {
- return _context!.Decrypt(buffer, ref offset, ref count);
- }
-
//
// This method assumes that a SSPI context is already in a good shape.
// For example it is either a fresh context or already authenticated context that needs renegotiation.
@@ -813,6 +803,117 @@ private void ReturnReadBufferIfEmpty()
_decryptedBytesCount = 0;
_decryptedBytesOffset = 0;
}
+ else if (_decryptedBytesCount == 0)
+ {
+ _decryptedBytesOffset = 0;
+ }
+ }
+
+
+ private bool HaveFullTlsFrame(out int frameSize)
+ {
+ if (_internalBufferCount < SecureChannel.ReadHeaderSize)
+ {
+ frameSize = int.MaxValue;
+ return false;
+ }
+
+ frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
+ return _internalBufferCount >= frameSize;
+ }
+
+
+ private async ValueTask EnsureFullTlsFrameAsync(TIOAdapter adapter)
+ where TIOAdapter : IReadWriteAdapter
+ {
+ int frameSize;
+ if (HaveFullTlsFrame(out frameSize))
+ {
+ return frameSize;
+ }
+
+ // We may have enough space to complete frame, but we may still do extra IO if the frame is small.
+ // So we will attempt larger read - that is trade of with extra copy.
+ // This may be updated at some point based on size of existing chunk, rented buffer and size of 'buffer'.
+ ResetReadBuffer();
+
+ // _internalOffset is 0 after ResetReadBuffer and we use _internalBufferCount to determined where to read.
+ while (_internalBufferCount < frameSize)
+ {
+ // We either don't have full frame or we don't have enough data to even determine the size.
+ int bytesRead = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false);
+ if (bytesRead == 0)
+ {
+ if (_internalBufferCount != 0)
+ {
+ // we got EOF in middle of TLS frame. Treat that as error.
+ throw new IOException(SR.net_io_eof);
+ }
+
+ return 0;
+ }
+
+ _internalBufferCount += bytesRead;
+ if (frameSize == int.MaxValue && _internalBufferCount > SecureChannel.ReadHeaderSize)
+ {
+ // recalculate frame size if needed e.g. we could not get it before.
+ frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
+ }
+ }
+
+ return frameSize;
+ }
+
+ private SecurityStatusPal DecryptData(int frameSize)
+ {
+ Debug.Assert(_decryptedBytesCount == 0);
+
+ // Set _decryptedBytesOffset/Count to the current frame we have (including header)
+ // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
+ _decryptedBytesOffset = _internalOffset;
+ _decryptedBytesCount = frameSize;
+ SecurityStatusPal status;
+
+ lock (_handshakeLock)
+ {
+ ThrowIfExceptionalOrNotAuthenticated();
+ status = _context!.Decrypt(new Span(_internalBuffer, _internalOffset, frameSize), out int decryptedOffset, out int decryptedCount);
+ _decryptedBytesCount = decryptedCount;
+ if (decryptedCount > 0)
+ {
+ _decryptedBytesOffset = _internalOffset + decryptedOffset;
+ }
+
+ if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
+ {
+ // The status indicates that peer wants to renegotiate. (Windows only)
+ // In practice, there can be some other reasons too - like TLS1.3 session creation
+ // of alert handling. We need to pass the data to lsass and it is not safe to do parallel
+ // write any more as that can change TLS state and the EncryptData() can fail in strange ways.
+
+ // To handle this we call DecryptData() under lock and we create TCS waiter.
+ // EncryptData() checks that under same lock and if it exist it will not call low-level crypto.
+ // Instead it will wait synchronously or asynchronously and it will try again after the wait.
+ // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over.
+ // If that happen before EncryptData() runs, _handshakeWaiter will be set to null
+ // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData()
+
+
+ if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0)
+ {
+ // create TCS only if we plan to proceed. If not, we will throw later outside of the lock.
+ // Tls1.3 does not have renegotiation. However on Windows this error code is used
+ // for session management e.g. anything lsass needs to see.
+ // We also allow it when explicitly requested using RenegotiateAsync().
+ _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ }
+ }
+ }
+
+ // Treat the bytes we just decrypted as consumed
+ ConsumeBufferedBytes(frameSize);
+
+ return status;
}
private async ValueTask ReadAsyncInternal(TIOAdapter adapter, Memory buffer, bool renegotiation = false)
@@ -826,109 +927,63 @@ private async ValueTask ReadAsyncInternal(TIOAdapter adapter, M
}
}
+ ThrowIfExceptionalOrNotAuthenticated();
+
Debug.Assert(_internalBuffer is null || _internalBufferCount > 0 || _decryptedBytesCount > 0, "_internalBuffer allocated when no data is buffered.");
+ int processedLength = 0;
+ int payloadBytes = 0;
try
{
- while (true)
+ if (_decryptedBytesCount != 0)
{
- if (_decryptedBytesCount != 0)
+ if (renegotiation)
{
- if (renegotiation)
- {
- throw new InvalidOperationException(SR.net_ssl_renegotiate_data);
- }
-
- return CopyDecryptedData(buffer);
+ throw new InvalidOperationException(SR.net_ssl_renegotiate_data);
}
- if (buffer.Length == 0 && _internalBuffer is null && !renegotiation)
+ processedLength = CopyDecryptedData(buffer);
+ if (processedLength == buffer.Length || !HaveFullTlsFrame(out payloadBytes))
{
- // User requested a zero-byte read, and we have no data available in the buffer for processing.
- // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read
- // for reduced memory consumption when data is not immediately available.
- // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation
- // until data is actually available from the underlying stream.
- // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will
- // complete immediately and won't save any memory, but will still function correctly.
- await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false);
+ // We either filled whole buffer or used all buffered frames.
+ return processedLength;
}
- ResetReadBuffer();
-
- // Read the next frame header.
- if (_internalBufferCount < SecureChannel.ReadHeaderSize)
- {
- // We don't have enough bytes buffered, so issue an initial read to try to get enough. This is
- // done in this method both to better consolidate error handling logic (the first read is the special
- // case that needs to differentiate reading 0 from > 0, and everything else needs to throw if it
- // doesn't read enough), and to minimize the chances that in the common case the FillBufferAsync
- // helper needs to yield and allocate a state machine.
- int readBytes = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false);
- if (readBytes == 0)
- {
- return 0;
- }
-
- _internalBufferCount += readBytes;
- if (_internalBufferCount < SecureChannel.ReadHeaderSize)
- {
- await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false);
- }
- }
- Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize);
+ buffer = buffer.Slice(processedLength);
+ }
- // Parse the frame header to determine the payload size (which includes the header size).
- int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
- if (payloadBytes < 0)
- {
- throw new IOException(SR.net_frame_read_size);
- }
+ if (_receivedEOF)
+ {
+ Debug.Assert(_internalBufferCount == 0);
+ // We received EOF during previous read but had buffered data to return.
+ return 0;
+ }
- // Read in the rest of the payload if we don't have it.
- if (_internalBufferCount < payloadBytes)
- {
- await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false);
- }
+ if (buffer.Length == 0 && _internalBuffer is null)
+ {
+ // User requested a zero-byte read, and we have no data available in the buffer for processing.
+ // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read
+ // for reduced memory consumption when data is not immediately available.
+ // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation
+ // until data is actually available from the underlying stream.
+ // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will
+ // complete immediately and won't save any memory, but will still function correctly.
+ await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false);
+ }
- // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
- // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
- _decryptedBytesOffset = _internalOffset;
- _decryptedBytesCount = payloadBytes;
+ Debug.Assert(_decryptedBytesCount == 0);
+ Debug.Assert(_decryptedBytesOffset == 0);
- SecurityStatusPal status;
- lock (_handshakeLock)
+ while (true)
+ {
+ payloadBytes = await EnsureFullTlsFrameAsync(adapter).ConfigureAwait(false);
+ if (payloadBytes == 0)
{
- status = DecryptData();
- if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
- {
- // The status indicates that peer wants to renegotiate. (Windows only)
- // In practice, there can be some other reasons too - like TLS1.3 session creation
- // of alert handling. We need to pass the data to lsass and it is not safe to do parallel
- // write any more as that can change TLS state and the EncryptData() can fail in strange ways.
-
- // To handle this we call DecryptData() under lock and we create TCS waiter.
- // EncryptData() checks that under same lock and if it exist it will not call low-level crypto.
- // Instead it will wait synchronously or asynchronously and it will try again after the wait.
- // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over.
- // If that happen before EncryptData() runs, _handshakeWaiter will be set to null
- // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData()
-
- if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0)
- {
- // create TCS only if we plan to proceed. If not, we will throw in block bellow outside of the lock.
- // Tls1.3 does not have renegotiation. However on Windows this error code is used
- // for session management e.g. anything lsass needs to see.
- // We also allow it when explicitly requested using RenegotiateAsync().
- _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- }
- }
+ _receivedEOF = true;
+ break;
}
- // Treat the bytes we just decrypted as consumed
- // Note, we won't do another buffer read until the decrypted bytes are processed
- ConsumeBufferedBytes(payloadBytes);
-
+ SecurityStatusPal status = DecryptData(payloadBytes);
if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
{
byte[]? extraBuffer = null;
@@ -962,12 +1017,50 @@ private async ValueTask ReadAsyncInternal(TIOAdapter adapter, M
if (status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired)
{
- return 0;
+ _receivedEOF = true;
+ break;
}
throw new IOException(SR.net_io_decrypt, SslStreamPal.GetException(status));
}
+
+ if (_decryptedBytesCount > 0)
+ {
+ // This will either copy data from rented buffer or adjust final buffer as needed.
+ // In both cases _decryptedBytesOffset and _decryptedBytesCount will be updated as needed.
+ int copyLength = CopyDecryptedData(buffer);
+ processedLength += copyLength;
+ if (copyLength == buffer.Length)
+ {
+ // We have more decrypted data after we filled provided buffer.
+ break;
+ }
+
+ buffer = buffer.Slice(copyLength);
+ }
+
+ if (processedLength == 0)
+ {
+ // We did not get any real data so far.
+ continue;
+ }
+
+ if (!HaveFullTlsFrame(out payloadBytes))
+ {
+ // We don't have another frame to process but we have some data to return to caller.
+ break;
+ }
+
+ TlsFrameHelper.TryGetFrameHeader(_internalBuffer.AsSpan(_internalOffset), ref _lastFrame.Header);
+ if (_lastFrame.Header.Type != TlsContentType.AppData)
+ {
+ // Alerts, handshake and anything else will be processed separately.
+ // This may not be necessary but it improves compatibility with older versions.
+ break;
+ }
}
+
+ return processedLength;
}
catch (Exception e)
{
@@ -1101,6 +1194,10 @@ private void ConsumeBufferedBytes(int byteCount)
_internalOffset += byteCount;
_internalBufferCount -= byteCount;
+ if (_internalBufferCount == 0)
+ {
+ _internalOffset = 0;
+ }
}
private int CopyDecryptedData(Memory buffer)
@@ -1116,6 +1213,11 @@ private int CopyDecryptedData(Memory buffer)
_decryptedBytesCount -= copyBytes;
}
+ if (_decryptedBytesCount == 0)
+ {
+ _decryptedBytesOffset = 0;
+ }
+
return copyBytes;
}
@@ -1333,7 +1435,7 @@ private int GetFrameSize(ReadOnlySpan buffer)
payloadSize = ((buffer[3] << 8) | buffer[4]) + 5;
break;
default:
- break;
+ throw new IOException(SR.net_frame_read_size);
}
return payloadSize;
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs
index 24f1e8e0e01b6..27c854c99168e 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs
@@ -112,17 +112,20 @@ public static SecurityStatusPal EncryptMessage(
public static SecurityStatusPal DecryptMessage(
SafeDeleteSslContext securityContext,
- byte[] buffer,
- ref int offset,
- ref int count)
+ Span buffer,
+ out int offset,
+ out int count)
{
+ offset = 0;
+ count = 0;
+
try
{
SafeSslHandle sslHandle = securityContext.SslContext;
- securityContext.Write(buffer.AsSpan(offset, count));
+ securityContext.Write(buffer);
- PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer.AsSpan(offset, count), out int read);
+ PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read);
if (ret == PAL_SSLStreamStatus.Error)
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError);
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
index 53c4d12c99ab8..0414082781c1e 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
@@ -143,22 +143,23 @@ public static SecurityStatusPal EncryptMessage(
public static SecurityStatusPal DecryptMessage(
SafeDeleteSslContext securityContext,
- byte[] buffer,
- ref int offset,
- ref int count)
+ Span buffer,
+ out int offset,
+ out int count)
{
+ offset = 0;
+ count = 0;
+
try
{
SafeSslHandle sslHandle = securityContext.SslContext;
-
- securityContext.Write(buffer.AsSpan(offset, count));
+ securityContext.Write(buffer);
unsafe
{
- fixed (byte* offsetInput = &buffer[offset])
+ fixed (byte* ptr = buffer)
{
- PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out int written);
-
+ PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, ptr, buffer.Length, out int written);
if (status < 0)
{
return new SecurityStatusPal(
@@ -167,6 +168,7 @@ public static SecurityStatusPal DecryptMessage(
}
count = written;
+ offset = 0;
switch (status)
{
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
index 98a21e128569c..020456131702f 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
@@ -62,11 +62,14 @@ public static SecurityStatusPal EncryptMessage(SafeDeleteSslContext securityCont
}
}
- public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, byte[] buffer, ref int offset, ref int count)
+ public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, Span buffer, out int offset, out int count)
{
+ offset = 0;
+ count = 0;
+
try
{
- int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, new Span(buffer, offset, count), out Interop.Ssl.SslErrorCode errorCode);
+ int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, buffer, out Interop.Ssl.SslErrorCode errorCode);
SecurityStatusPal retVal = MapNativeErrorCode(errorCode);
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs
index 59a781b470a9e..4060296c4ed87 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs
@@ -313,7 +313,7 @@ public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteSslContext secur
}
}
- public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, byte[] buffer, ref int offset, ref int count)
+ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, Span buffer, out int offset, out int count)
{
const int NumSecBuffers = 4; // data + empty + empty + empty
fixed (byte* bufferPtr = buffer)
@@ -321,8 +321,8 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu
Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[0];
dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
- dataBuffer->pvBuffer = (IntPtr)bufferPtr + offset;
- dataBuffer->cbBuffer = count;
+ dataBuffer->pvBuffer = (IntPtr)bufferPtr;
+ dataBuffer->cbBuffer = buffer.Length;
for (int i = 1; i < NumSecBuffers; i++)
{
@@ -341,6 +341,7 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu
// Decrypt may repopulate the sec buffers, likely with header + data + trailer + empty.
// We need to find the data.
count = 0;
+ offset = 0;
for (int i = 0; i < NumSecBuffers; i++)
{
// Successfully decoded data and placed it at the following position in the buffer,
@@ -351,6 +352,7 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu
offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferPtr);
count = unmanagedBuffer[i].cbBuffer;
+ // output is ignored on Windows. We always decrypt in place and we set outputOffset to indicate where the data start.
Debug.Assert(offset >= 0 && count >= 0, $"Expected offset and count greater than 0, got {offset} and {count}");
Debug.Assert(checked(offset + count) <= buffer.Length, $"Expected offset+count <= buffer.Length, got {offset}+{count}>={buffer.Length}");
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs
index 8104387dc07ff..8f5b5ed2a07c5 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs
@@ -90,6 +90,11 @@ internal struct TlsFrameHeader
public int Length;
public override string ToString() => $"{Version}:{Type}[{Length}]";
+
+ public int GetFrameSize()
+ {
+ return Length + TlsFrameHelper.HeaderSize;
+ }
}
internal static class TlsFrameHelper
diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs
deleted file mode 100644
index 2389188bbef5e..0000000000000
--- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Net.Test.Common;
-using System.Security.Authentication;
-using System.Threading.Tasks;
-
-using Xunit;
-using Xunit.Abstractions;
-
-namespace System.Net.Security.Tests
-{
- using Configuration = System.Net.Test.Common.Configuration;
-
- public class SchSendAuxRecordTest
- {
- readonly ITestOutputHelper _output;
-
- public SchSendAuxRecordTest(ITestOutputHelper output)
- {
- _output = output;
- }
-
- [Fact]
- [PlatformSpecific(TestPlatforms.Windows)]
- public async Task SslStream_ClientAndServerUsesAuxRecord_Ok()
- {
- using (var server = new HttpsTestServer())
- {
- server.Start();
-
- var tasks = new Task[2];
-
- bool serverAuxRecordDetected = false;
- bool serverAuxRecordDetectedInconclusive = false;
- int serverTotalBytesReceived = 0;
- int serverChunks = 0;
-
- tasks[0] = server.AcceptHttpsClientAsync((requestString) =>
- {
- serverTotalBytesReceived += requestString.Length;
-
- if (serverTotalBytesReceived == 1 && serverChunks == 0)
- {
- serverAuxRecordDetected = true;
- }
-
- serverChunks++;
-
- // Test is inconclusive if any non-CBC cipher is used:
- if (server.Stream.CipherAlgorithm == CipherAlgorithmType.None ||
- server.Stream.CipherAlgorithm == CipherAlgorithmType.Null ||
- server.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4)
- {
- serverAuxRecordDetectedInconclusive = true;
- }
-
- if (serverTotalBytesReceived < 5)
- {
- return Task.FromResult(null);
- }
- else
- {
- return Task.FromResult(HttpsTestServer.Options.DefaultResponseString);
- }
- });
-
-
- var clientOptions = new HttpsTestClient.Options(new IPEndPoint(IPAddress.Loopback, server.Port));
- clientOptions.AllowedProtocols = SslProtocols.Tls;
-
- clientOptions.IgnoreSslPolicyErrors =
- SslPolicyErrors.RemoteCertificateNameMismatch | SslPolicyErrors.RemoteCertificateChainErrors;
-
- var client = new HttpsTestClient(clientOptions);
-
- bool clientAuxRecordDetected = false;
- bool clientAuxRecordDetectedInconclusive = false;
- int clientTotalBytesReceived = 0;
- int clientChunks = 0;
-
- tasks[1] = client.HttpsRequestAsync((responseString) =>
- {
- if (responseString == null)
- {
- string requestString = string.Format(
- HttpsTestClient.Options.DefaultRequestStringTemplate,
- clientOptions.ServerName);
-
- return Task.FromResult(requestString);
- }
-
- clientTotalBytesReceived += responseString.Length;
-
- if (clientTotalBytesReceived == 1 && clientChunks == 0)
- {
- clientAuxRecordDetected = true;
- }
-
- // Test is inconclusive if any non-CBC cipher is used:
- if (client.Stream.CipherAlgorithm == CipherAlgorithmType.None ||
- client.Stream.CipherAlgorithm == CipherAlgorithmType.Null ||
- client.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4)
- {
- clientAuxRecordDetectedInconclusive = true;
- }
-
- return Task.FromResult(null);
- });
-
- await Task.WhenAll(tasks).WaitAsync(TestConfiguration.PassingTestTimeout);
-
- if (serverAuxRecordDetectedInconclusive || clientAuxRecordDetectedInconclusive)
- {
- _output.WriteLine("Test inconclusive: The Operating system preferred a non-CBC or Null cipher.");
- }
- else
- {
- Assert.True(serverAuxRecordDetected, "Server reports: Client auxiliary record not detected.");
- Assert.True(clientAuxRecordDetected, "Client reports: Server auxiliary record not detected.");
- }
- }
- }
- }
-}
diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj
index bb131408de325..3dc537ad91deb 100644
--- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj
+++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj
@@ -100,7 +100,6 @@
-
diff --git a/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs
index 6a75b0bf2bf6e..91d40fc744837 100644
--- a/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs
+++ b/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs
@@ -68,7 +68,7 @@ public void LocalCertificateSelectionCallback_Get_Set_Succeeds()
{
Assert.Null(_clientOptions.LocalCertificateSelectionCallback);
- LocalCertificateSelectionCallback callback = (sender, host, localCertificates, remoteCertificate, issuers) => { return new X509Certificate(); };
+ LocalCertificateSelectionCallback callback = (sender, host, localCertificates, remoteCertificate, issuers) => default;
_clientOptions.LocalCertificateSelectionCallback = callback;
Assert.Equal(callback, _clientOptions.LocalCertificateSelectionCallback);
@@ -109,7 +109,7 @@ public void ServerCertificate_Get_Set_Succeeds()
_serverOptions.ServerCertificate = null;
Assert.Null(_serverOptions.ServerCertificate);
- X509Certificate cert = new X509Certificate();
+ X509Certificate cert = new X509Certificate2(stackalloc byte[0]);
_serverOptions.ServerCertificate = cert;
Assert.Equal(cert, _serverOptions.ServerCertificate);
diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
index 0ea7f6b7ce72d..8ed4a71a4e805 100644
--- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
+++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
@@ -760,12 +760,19 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, int timeToLiv
public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IPAddress localAddress) { }
public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; }
public System.Threading.Tasks.Task ReceiveAsync() { throw null; }
+ public System.Threading.Tasks.ValueTask ReceiveAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
public int Send(byte[] dgram, int bytes) { throw null; }
+ public int Send(System.ReadOnlySpan datagram) {throw null; }
public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; }
+ public int Send(System.ReadOnlySpan datagram, System.Net.IPEndPoint? endPoint) { throw null; }
public int Send(byte[] dgram, int bytes, string? hostname, int port) { throw null; }
+ public int Send(System.ReadOnlySpan datagram, string? hostname, int port) { throw null; }
public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes) { throw null; }
+ public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Threading.CancellationToken cancellationToken = default) { throw null; }
public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; }
+ public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Net.IPEndPoint? endPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; }
public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) { throw null; }
+ public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, string? hostname, int port, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial struct UdpReceiveResult : System.IEquatable
{
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
index bda1217e01f20..699a447837e0d 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
@@ -74,7 +74,6 @@ public partial class SocketAsyncEventArgs : EventArgs, IDisposable
private Socket? _currentSocket;
private bool _userSocket; // if false when performing Connect, _currentSocket should be disposed
private bool _disposeCalled;
- private protected bool _disableTelemetry;
// Controls thread safety via Interlocked.
private const int Configuring = -1;
@@ -202,7 +201,7 @@ public int BytesTransferred
private void OnCompletedInternal()
{
- if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry();
+ if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry();
OnCompleted(this);
}
@@ -813,11 +812,8 @@ caughtException is OperationCanceledException ||
}
// Complete the operation.
- if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry)
- {
- LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred);
- AfterConnectAcceptTelemetry();
- }
+ if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred);
+
Complete();
// Clean up after our temporary arguments.
@@ -842,12 +838,7 @@ private sealed class MultiConnectSocketAsyncEventArgs : SocketAsyncEventArgs, IV
private ManualResetValueTaskSourceCore _mrvtsc;
private int _isCompleted;
- public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false)
- {
- // Instances of this type are an implementation detail of an overarching connect operation.
- // We don't want to emit telemetry specific to operations on this inner instance.
- _disableTelemetry = true;
- }
+ public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) { }
public void GetResult(short token) => _mrvtsc.GetResult(token);
public ValueTaskSourceStatus GetStatus(short token) => _mrvtsc.GetStatus(token);
@@ -968,7 +959,7 @@ internal void FinishOperationSyncSuccess(int bytesTransferred, SocketFlags flags
break;
}
- if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred);
+ if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred);
Complete();
}
@@ -1003,7 +994,7 @@ private void FinishOperationSync(SocketError socketError, int bytesTransferred,
FinishOperationSyncFailure(socketError, bytesTransferred, flags);
}
- if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry();
+ if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry();
}
private static void LogBytesTransferEvents(SocketType? socketType, SocketAsyncOperation operation, int bytesTransferred)
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs
index 7b7641f2a4003..cb1e77f17aebc 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs
@@ -113,7 +113,7 @@ public void AcceptStart(EndPoint address)
{
if (IsEnabled(EventLevel.Informational, EventKeywords.All))
{
- AcceptStart(address.ToString());
+ AcceptStart(address.Serialize().ToString());
}
}
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs
index e27a77da1f81d..246483d0815fb 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs
@@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using System.Runtime.Versioning;
+using System.Threading;
namespace System.Net.Sockets
{
@@ -600,9 +601,46 @@ public void DropMulticastGroup(IPAddress multicastAddr, int ifindex)
public Task SendAsync(byte[] datagram, int bytes) =>
SendAsync(datagram, bytes, null);
+ ///
+ /// Sends a UDP datagram asynchronously to a remote host.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ ///
+ /// The token to monitor for cancellation requests. The default value is None.
+ ///
+ /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.
+ /// The is closed.
+ /// An error occurred when accessing the socket.
+ public ValueTask SendAsync(ReadOnlyMemory datagram, CancellationToken cancellationToken = default) =>
+ SendAsync(datagram, null, cancellationToken);
+
public Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) =>
SendAsync(datagram, bytes, GetEndpoint(hostname, port));
+ ///
+ /// Sends a UDP datagram asynchronously to a remote host.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ ///
+ /// The name of the remote host to which you intend to send the datagram.
+ ///
+ ///
+ /// The remote port number with which you intend to communicate.
+ ///
+ ///
+ /// The token to monitor for cancellation requests. The default value is None.
+ ///
+ /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.
+ /// The has already established a default remote host.
+ /// The is closed.
+ /// An error occurred when accessing the socket.
+ public ValueTask SendAsync(ReadOnlyMemory datagram, string? hostname, int port, CancellationToken cancellationToken = default) =>
+ SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken);
+
public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint)
{
ValidateDatagram(datagram, bytes, endPoint);
@@ -618,6 +656,39 @@ public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint)
}
}
+ ///
+ /// Sends a UDP datagram asynchronously to a remote host.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ ///
+ /// An that represents the host and port to which to send the datagram.
+ ///
+ ///
+ /// The token to monitor for cancellation requests. The default value is None.
+ ///
+ /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.
+ /// has already established a default remote host and is not .
+ /// The is closed.
+ /// An error occurred when accessing the socket.
+ public ValueTask SendAsync(ReadOnlyMemory datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default)
+ {
+ ThrowIfDisposed();
+
+ if (endPoint is null)
+ {
+ return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken);
+ }
+ if (_active)
+ {
+ // Do not allow sending packets to arbitrary host when connected.
+ throw new InvalidOperationException(SR.net_udpconnected);
+ }
+ CheckForBroadcast(endPoint.Address);
+ return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken);
+ }
+
public Task ReceiveAsync()
{
ThrowIfDisposed();
@@ -639,6 +710,36 @@ async Task WaitAndWrap(Task task)
}
}
+ ///
+ /// Returns a UDP datagram asynchronously that was sent by a remote host.
+ ///
+ ///
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A representing the asynchronous operation.
+ /// The underlying has been closed.
+ /// An error occurred when accessing the socket.
+ public ValueTask ReceiveAsync(CancellationToken cancellationToken)
+ {
+ ThrowIfDisposed();
+
+ return WaitAndWrap(_clientSocket.ReceiveFromAsync(
+ _buffer,
+ SocketFlags.None,
+ _family == AddressFamily.InterNetwork ? IPEndPointStatics.Any : IPEndPointStatics.IPv6Any, cancellationToken));
+
+ async ValueTask WaitAndWrap(ValueTask task)
+ {
+ SocketReceiveFromResult result = await task.ConfigureAwait(false);
+
+ byte[] buffer = result.ReceivedBytes < MaxUDPSize ?
+ _buffer.AsSpan(0, result.ReceivedBytes).ToArray() :
+ _buffer;
+
+ return new UdpReceiveResult(buffer, (IPEndPoint)result.RemoteEndPoint);
+ }
+ }
+
private void CreateClientSocket()
{
// Common initialization code.
@@ -892,45 +993,59 @@ public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint)
return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint);
}
-
- // Sends a UDP datagram to the specified port on the specified remote host.
- public int Send(byte[] dgram, int bytes, string? hostname, int port)
+ ///
+ /// Sends a UDP datagram to the host at the specified remote endpoint.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ ///
+ /// An that represents the host and port to which to send the datagram.
+ ///
+ /// The number of bytes sent.
+ /// has already established a default remote host and is not .
+ /// is closed.
+ /// An error occurred when accessing the socket.
+ public int Send(ReadOnlySpan datagram, IPEndPoint? endPoint)
{
ThrowIfDisposed();
- if (dgram == null)
- {
- throw new ArgumentNullException(nameof(dgram));
- }
- if (_active && ((hostname != null) || (port != 0)))
+ if (_active && endPoint != null)
{
// Do not allow sending packets to arbitrary host when connected
throw new InvalidOperationException(SR.net_udpconnected);
}
- if (hostname == null || port == 0)
- {
- return Client.Send(dgram, 0, bytes, SocketFlags.None);
- }
-
- IPAddress[] addresses = Dns.GetHostAddresses(hostname);
-
- int i = 0;
- for (; i < addresses.Length && !IsAddressFamilyCompatible(addresses[i].AddressFamily); i++)
+ if (endPoint == null)
{
- ; // just count the addresses
+ return Client.Send(datagram, SocketFlags.None);
}
- if (addresses.Length == 0 || i == addresses.Length)
- {
- throw new ArgumentException(SR.net_invalidAddressList, nameof(hostname));
- }
+ CheckForBroadcast(endPoint.Address);
- CheckForBroadcast(addresses[i]);
- IPEndPoint ipEndPoint = new IPEndPoint(addresses[i], port);
- return Client.SendTo(dgram, 0, bytes, SocketFlags.None, ipEndPoint);
+ return Client.SendTo(datagram, SocketFlags.None, endPoint);
}
+ // Sends a UDP datagram to the specified port on the specified remote host.
+ public int Send(byte[] dgram, int bytes, string? hostname, int port) => Send(dgram, bytes, GetEndpoint(hostname, port));
+
+ ///
+ /// Sends a UDP datagram to a specified port on a specified remote host.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ ///
+ /// The name of the remote host to which you intend to send the datagram.
+ ///
+ ///
+ /// The remote port number with which you intend to communicate.
+ ///
+ /// The number of bytes sent.
+ /// The has already established a default remote host.
+ /// The is closed.
+ /// An error occurred when accessing the socket.
+ public int Send(ReadOnlySpan datagram, string? hostname, int port) => Send(datagram, GetEndpoint(hostname, port));
// Sends a UDP datagram to a remote host.
public int Send(byte[] dgram, int bytes)
@@ -950,6 +1065,29 @@ public int Send(byte[] dgram, int bytes)
return Client.Send(dgram, 0, bytes, SocketFlags.None);
}
+ ///
+ /// Sends a UDP datagram to a remote host.
+ ///
+ ///
+ /// An of Type that specifies the UDP datagram that you intend to send.
+ ///
+ /// The number of bytes sent.
+ /// The has not established a default remote host.
+ /// The is closed.
+ /// An error occurred when accessing the socket.
+ public int Send(ReadOnlySpan datagram)
+ {
+ ThrowIfDisposed();
+
+ if (!_active)
+ {
+ // only allowed on connected socket
+ throw new InvalidOperationException(SR.net_notconnected);
+ }
+
+ return Client.Send(datagram, SocketFlags.None);
+ }
+
private void ThrowIfDisposed()
{
if (_disposed)
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs
index 25af4a45bbaa1..55535f0583009 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs
@@ -6,7 +6,7 @@
namespace System.Net.Sockets
{
///
- /// Presents UDP receive result information from a call to the method
+ /// Presents UDP receive result information from a call to the and method
///
public struct UdpReceiveResult : IEquatable
{
diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs
index ce83af9bb677d..252b862bce1ab 100644
--- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs
+++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs
@@ -11,8 +11,8 @@ public sealed class SendReceiveUdpClient : MemberDatas
{
[OuterLoop]
[Theory]
- [MemberData(nameof(Loopbacks))]
- public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress)
+ [MemberData(nameof(LoopbacksAndUseMemory))]
+ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress, bool useMemoryOverload)
{
IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress;
@@ -66,7 +66,7 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA
random.NextBytes(sendBuffer);
sendBuffer[0] = (byte)sentDatagrams;
- int sent = await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint);
+ int sent = useMemoryOverload ? await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint) : await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint);
Assert.True(receiverAck.Wait(AckTimeout));
receiverAck.Reset();
@@ -85,5 +85,13 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA
}
}
}
+
+ public static readonly object[][] LoopbacksAndUseMemory = new object[][]
+ {
+ new object[] { IPAddress.IPv6Loopback, true },
+ new object[] { IPAddress.IPv6Loopback, false },
+ new object[] { IPAddress.Loopback, true },
+ new object[] { IPAddress.Loopback, false },
+ };
}
}
diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs
index ad17579f90e6a..7b507a18f4037 100644
--- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs
+++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs
@@ -8,7 +8,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
-using Microsoft.DotNet.XUnitExtensions;
using Xunit;
using Xunit.Abstractions;
@@ -117,14 +116,9 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
await WaitForEventCountersAsync(events);
});
- Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself
-
- VerifyStartStopEvents(events, connect: true, expectedCount: 1);
- VerifyStartStopEvents(events, connect: false, expectedCount: 1);
-
- Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed");
- Assert.DoesNotContain(events, e => e.Event.EventName == "AcceptFailed");
+ VerifyEvents(events, connect: true, expectedCount: 1);
+ VerifyEvents(events, connect: false, expectedCount: 1);
VerifyEventCounters(events, connectCount: 1);
}, connectMethod, acceptMethod).Dispose();
}
@@ -153,12 +147,8 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
await WaitForEventCountersAsync(events);
});
- Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself
-
- VerifyStartStopEvents(events, connect: true, expectedCount: 1);
-
- Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed");
+ VerifyEvents(events, connect: true, expectedCount: 1);
VerifyEventCounters(events, connectCount: 1, connectOnly: true);
}, connectMethod, useDnsEndPoint.ToString()).Dispose();
}
@@ -169,12 +159,6 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
[MemberData(nameof(SocketMethods_WithBools_MemberData))]
public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint)
{
- if (useDnsEndPoint)
- {
- // [ActiveIssue("https://github.com/dotnet/runtime/issues/43931")]
- throw new SkipTestException("https://github.com/dotnet/runtime/issues/43931");
- }
-
RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) =>
{
EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345);
@@ -207,7 +191,10 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
await WaitForEventCountersAsync(events);
});
- VerifyConnectFailureEvents(events);
+ // For DNS endpoints, we may see multiple Start/Failure/Stop events
+ int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1;
+ VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true);
+ VerifyEventCounters(events, connectCount: 0);
}, connectMethod, useDnsEndPoint.ToString()).Dispose();
}
@@ -216,12 +203,6 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
[MemberData(nameof(SocketMethods_MemberData))]
public void EventSource_SocketAcceptFailure_LogsAcceptFailed(string acceptMethod)
{
- if (acceptMethod == "Sync" && PlatformDetection.IsRedHatFamily7)
- {
- // [ActiveIssue("https://github.com/dotnet/runtime/issues/42686")]
- throw new SkipTestException("Disposing a Socket performing a sync operation can hang on RedHat7 systems");
- }
-
RemoteExecutor.Invoke(async acceptMethod =>
{
using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1);
@@ -246,18 +227,8 @@ await Assert.ThrowsAnyAsync(async () =>
await WaitForEventCountersAsync(events);
});
- Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself
-
- VerifyStartStopEvents(events, connect: false, expectedCount: 1);
-
- (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "AcceptFailed");
- Assert.Equal(2, failed.Event.Payload.Count);
- Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0]));
- Assert.IsType(failed.Event.Payload[1]);
-
- (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "AcceptStart");
- Assert.Equal(startActivityId, failed.ActivityId);
+ VerifyEvents(events, connect: false, expectedCount: 1, shouldHaveFailures: true);
VerifyEventCounters(events, connectCount: 0);
}, acceptMethod).Dispose();
}
@@ -270,12 +241,6 @@ await Assert.ThrowsAnyAsync(async () =>
[InlineData("Eap", false)]
public void EventSource_ConnectAsyncCanceled_LogsConnectFailed(string connectMethod, bool useDnsEndPoint)
{
- if (useDnsEndPoint)
- {
- // [ActiveIssue("https://github.com/dotnet/runtime/issues/46030")]
- throw new SkipTestException("https://github.com/dotnet/runtime/issues/46030");
- }
-
RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) =>
{
EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345);
@@ -326,27 +291,13 @@ await Assert.ThrowsAnyAsync(async () =>
await WaitForEventCountersAsync(events);
});
- VerifyConnectFailureEvents(events);
+ // For DNS endpoints, we may see multiple Start/Failure/Stop events
+ int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1;
+ VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true);
+ VerifyEventCounters(events, connectCount: 0);
}, connectMethod, useDnsEndPoint.ToString()).Dispose();
}
- private static void VerifyConnectFailureEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events)
- {
- Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself
-
- VerifyStartStopEvents(events, connect: true, expectedCount: 1);
-
- (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "ConnectFailed");
- Assert.Equal(2, failed.Event.Payload.Count);
- Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0]));
- Assert.IsType(failed.Event.Payload[1]);
-
- (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "ConnectStart");
- Assert.Equal(startActivityId, failed.ActivityId);
-
- VerifyEventCounters(events, connectCount: 0);
- }
-
[OuterLoop]
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void EventSource_EventsRaisedAsExpected()
@@ -374,48 +325,21 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn
await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, false).ConfigureAwait(false);
await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, true).ConfigureAwait(false);
- await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false);
- await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false);
+ await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false);
+ await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false);
await new NetworkStreamTest().CopyToAsync_AllDataCopied(4096, true).ConfigureAwait(false);
await new NetworkStreamTest().Timeout_Roundtrips().ConfigureAwait(false);
await WaitForEventCountersAsync(events);
});
- Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself
-
- VerifyStartStopEvents(events, connect: true, expectedCount: 10);
-
- Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed");
+ VerifyEvents(events, connect: true, expectedCount: 10);
VerifyEventCounters(events, connectCount: 10, shouldHaveTransferedBytes: true, shouldHaveDatagrams: true);
}
}).Dispose();
}
- private static void VerifyStartStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int expectedCount)
- {
- string startName = connect ? "ConnectStart" : "AcceptStart";
- (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == startName).ToArray();
- Assert.Equal(expectedCount, starts.Length);
- foreach ((EventWrittenEventArgs Event, _) in starts)
- {
- object startPayload = Assert.Single(Event.Payload);
- Assert.False(string.IsNullOrWhiteSpace(startPayload as string));
- }
-
- string stopName = connect ? "ConnectStop" : "AcceptStop";
- (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == stopName).ToArray();
- Assert.Equal(expectedCount, stops.Length);
- Assert.All(stops, stop => Assert.Empty(stop.Event.Payload));
-
- for (int i = 0; i < expectedCount; i++)
- {
- Assert.NotEqual(Guid.Empty, starts[i].ActivityId);
- Assert.Equal(starts[i].ActivityId, stops[i].ActivityId);
- }
- }
-
private static async Task WaitForEventAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string name)
{
DateTime startTime = DateTime.UtcNow;
@@ -452,6 +376,76 @@ static bool IsBytesSentEventCounter(EventWrittenEventArgs e)
}
}
+ private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int? expectedCount, bool shouldHaveFailures = false)
+ {
+ bool start = false;
+ Guid startGuid = Guid.Empty;
+ bool seenFailures = false;
+ bool seenFailureAfterStart = false;
+ int numberOfStops = 0;
+
+ foreach ((EventWrittenEventArgs Event, Guid ActivityId) in events)
+ {
+ Assert.False(Event.EventId == 0, $"Received an error event from EventSource: {Event.Message}");
+
+ if (Event.EventName.Contains("Connect") != connect)
+ {
+ continue;
+ }
+
+ switch (Event.EventName)
+ {
+ case "ConnectStart":
+ case "AcceptStart":
+ Assert.False(start, "Start without a Stop");
+ Assert.NotEqual(Guid.Empty, ActivityId);
+ startGuid = ActivityId;
+ seenFailureAfterStart = false;
+ start = true;
+
+ string startAddress = Assert.IsType(Assert.Single(Event.Payload));
+ Assert.Matches(@"^InterNetwork.*?:\d\d:{(?:\d{1,3},?)+}$", startAddress);
+ break;
+
+ case "ConnectStop":
+ case "AcceptStop":
+ Assert.True(start, "Stop without a Start");
+ Assert.Equal(startGuid, ActivityId);
+ startGuid = Guid.Empty;
+ numberOfStops++;
+ start = false;
+
+ Assert.Empty(Event.Payload);
+ break;
+
+ case "ConnectFailed":
+ case "AcceptFailed":
+ Assert.True(start, "Failed should come between Start and Stop");
+ Assert.False(seenFailureAfterStart, "Start may only have one Failed event");
+ Assert.Equal(startGuid, ActivityId);
+ seenFailureAfterStart = true;
+ seenFailures = true;
+
+ Assert.Equal(2, Event.Payload.Count);
+ Assert.True(Enum.IsDefined((SocketError)Event.Payload[0]));
+ Assert.IsType(Event.Payload[1]);
+ break;
+ }
+ }
+
+ Assert.False(start, "Start without a Stop");
+ Assert.Equal(shouldHaveFailures, seenFailures);
+
+ if (expectedCount.HasValue)
+ {
+ Assert.Equal(expectedCount, numberOfStops);
+ }
+ else
+ {
+ Assert.NotEqual(0, numberOfStops);
+ }
+ }
+
private static void VerifyEventCounters(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, int connectCount, bool connectOnly = false, bool shouldHaveTransferedBytes = false, bool shouldHaveDatagrams = false)
{
Dictionary eventCounters = events
diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs
index 3b4479629507b..66f69e1ce70da 100644
--- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs
+++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs
@@ -59,7 +59,6 @@ public void Ctor_NullEndpoint_Throws()
AssertExtensions.Throws("localEP", () => new UdpClient(null));
}
- [OuterLoop]
[Fact]
public void Ctor_CanSend()
{
@@ -70,7 +69,6 @@ public void Ctor_CanSend()
}
}
- [OuterLoop]
[Fact]
public void Ctor_Int_CanSend()
{
@@ -88,7 +86,6 @@ public void Ctor_Int_CanSend()
}
}
- [OuterLoop]
[Fact]
public void Ctor_IntAddressFamily_IPv4_CanSend()
{
@@ -106,7 +103,6 @@ public void Ctor_IntAddressFamily_IPv4_CanSend()
}
}
- [OuterLoop]
[Fact]
public void Ctor_IntAddressFamily_IPv6_CanSend()
{
@@ -124,7 +120,6 @@ public void Ctor_IntAddressFamily_IPv6_CanSend()
}
}
- [OuterLoop]
[Fact]
public void Ctor_IPEndPoint_CanSend()
{
@@ -142,7 +137,6 @@ public void Ctor_IPEndPoint_CanSend()
}
}
- [OuterLoop]
[Fact]
public void Ctor_StringInt_CanSend()
{
@@ -191,6 +185,21 @@ public void DisposeClose_OperationsThrow(bool close)
Assert.Throws(() => udpClient.Send(null, 0, remoteEP));
Assert.Throws(() => udpClient.Send(null, 0));
Assert.Throws(() => udpClient.Send(null, 0, "localhost", 0));
+
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), remoteEP));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan()));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), "localhost", 0));
+
+ Assert.Throws(() => {udpClient.SendAsync(null, 0, remoteEP);});
+ Assert.Throws(() => {udpClient.SendAsync(null, 0);});
+ Assert.Throws(() => {udpClient.SendAsync(null, 0, "localhost", 0);});
+
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP));
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory()));
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0));
+
+ Assert.Throws(() => {udpClient.ReceiveAsync();});
+ Assert.Throws(() => udpClient.ReceiveAsync(default));
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))]
@@ -321,7 +330,6 @@ public void InvalidArguments_Throw()
}
}
- [OuterLoop]
[Fact]
public void BeginSend_NegativeBytes_Throws()
{
@@ -337,7 +345,6 @@ public void BeginSend_NegativeBytes_Throws()
}
}
- [OuterLoop]
[Fact]
public void BeginSend_BytesMoreThanArrayLength_Throws()
{
@@ -353,7 +360,6 @@ public void BeginSend_BytesMoreThanArrayLength_Throws()
}
}
- [OuterLoop]
[Fact]
public void BeginSend_AsyncOperationCompletes_Success()
{
@@ -377,8 +383,12 @@ public void Send_InvalidArguments_Throws()
AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, "localhost", 0));
AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, new IPEndPoint(IPAddress.Loopback, 0)));
Assert.Throws(() => udpClient.Send(new byte[1], 1));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1])));
udpClient.Active = true;
Assert.Throws(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0)));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)));
+ Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));});
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)));
}
}
@@ -389,10 +399,17 @@ public void Send_InvalidArguments_StringInt_Throws()
using (var udpClient = new UdpClient("localhost", 0))
{
Assert.Throws(() => udpClient.Send(new byte[1], 1, "localhost", 0));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), "localhost", 0));
+ Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, "localhost", 0);});
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0));
+
+ Assert.Throws(() => udpClient.Send(new byte[1], 1, null, UnusedPort));
+ Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), null, UnusedPort));
+ Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, null, UnusedPort);});
+ Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), null, UnusedPort));
}
}
- [OuterLoop]
[Fact]
public void Client_Idempotent()
{
@@ -421,7 +438,6 @@ public void Connect_InvalidArguments_Throws()
}
}
- [OuterLoop]
[Fact]
public async Task ConnectAsync_StringHost_Success()
{
@@ -431,7 +447,6 @@ public async Task ConnectAsync_StringHost_Success()
}
}
- [OuterLoop]
[Fact]
public async Task ConnectAsync_IPAddressHost_Success()
{
@@ -441,7 +456,6 @@ public async Task ConnectAsync_IPAddressHost_Success()
}
}
- [OuterLoop]
[Fact]
public void Connect_StringHost_Success()
{
@@ -451,7 +465,6 @@ public void Connect_StringHost_Success()
}
}
- [OuterLoop]
[Fact]
public void Connect_IPAddressHost_Success()
{
@@ -468,7 +481,6 @@ private void AsyncCompleted(IAsyncResult ar)
_waitHandle.Set();
}
- [OuterLoop]
[Theory]
[PlatformSpecific(TestPlatforms.Windows)] // Udp.AllowNatTraversal only supported on Windows
[InlineData(true, IPProtectionLevel.Unrestricted)]
@@ -482,7 +494,6 @@ public void AllowNatTraversal_Windows(bool allow, IPProtectionLevel resultLevel)
}
}
- [OuterLoop]
[Theory]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Udp.AllowNatTraversal throws PNSE on Unix
[InlineData(true)]
@@ -495,7 +506,6 @@ public void AllowNatTraversal_AnyUnix(bool allow)
}
}
- [OuterLoop]
[Theory]
[InlineData(false)]
[InlineData(true)]
@@ -507,32 +517,55 @@ public void Send_Receive_Success(bool ipv4)
using (var sender = new UdpClient(new IPEndPoint(address, 0)))
{
sender.Send(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+ AssertReceive(receiver);
- IPEndPoint remoteEP = null;
- byte[] data = receiver.Receive(ref remoteEP);
- Assert.NotNull(remoteEP);
- Assert.InRange(data.Length, 1, int.MaxValue);
+ sender.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+ AssertReceive(receiver);
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+ public void Send_Receive_With_HostName_Success(bool ipv4)
+ {
+ IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+ using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+ using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+ {
+ sender.Send(new byte[1], 1, "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+ AssertReceive(receiver);
+
+ sender.Send(new ReadOnlySpan(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+ AssertReceive(receiver);
}
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
- [OuterLoop]
public void Send_Receive_Connected_Success()
{
using (var receiver = new UdpClient("localhost", 0))
using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
{
sender.Send(new byte[1], 1);
+ AssertReceive(receiver);
- IPEndPoint remoteEP = null;
- byte[] data = receiver.Receive(ref remoteEP);
- Assert.NotNull(remoteEP);
- Assert.InRange(data.Length, 1, int.MaxValue);
+ sender.Send(new ReadOnlySpan(new byte[1]));
+ AssertReceive(receiver);
}
}
- [OuterLoop]
+ private static void AssertReceive(UdpClient receiver)
+ {
+ IPEndPoint remoteEP = null;
+ byte[] data = receiver.Receive(ref remoteEP);
+ Assert.NotNull(remoteEP);
+ Assert.InRange(data.Length, 1, int.MaxValue);
+ }
+
[Theory]
[InlineData(false)]
[InlineData(true)]
@@ -549,7 +582,6 @@ public void Send_Available_Success(bool ipv4)
}
}
- [OuterLoop]
[Theory]
[InlineData(false)]
[InlineData(true)]
@@ -571,7 +603,6 @@ public void BeginEndSend_BeginEndReceive_Success(bool ipv4)
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
- [OuterLoop]
public void BeginEndSend_BeginEndReceive_Connected_Success()
{
using (var receiver = new UdpClient("localhost", 0))
@@ -586,7 +617,6 @@ public void BeginEndSend_BeginEndReceive_Connected_Success()
}
}
- [OuterLoop]
[Theory]
[InlineData(false)]
[InlineData(true)]
@@ -598,28 +628,114 @@ public async Task SendAsync_ReceiveAsync_Success(bool ipv4)
using (var sender = new UdpClient(new IPEndPoint(address, 0)))
{
await sender.SendAsync(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+ await AssertReceiveAsync(receiver);
+
+ await sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+ await AssertReceiveAsync(receiver);
+ }
+ }
- UdpReceiveResult result = await receiver.ReceiveAsync();
- Assert.NotNull(result.RemoteEndPoint);
- Assert.NotNull(result.Buffer);
- Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+ public async Task SendAsync_ReceiveAsync_With_HostName_Success(bool ipv4)
+ {
+ IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+ using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+ using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+ {
+ await sender.SendAsync(new byte[1], "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+ await AssertReceiveAsync(receiver);
+
+ await sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+ await AssertReceiveAsync(receiver);
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task ReceiveAsync_Cancel_Throw(bool ipv4)
+ {
+ IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+ using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+ {
+ using (var timeoutCts = new CancellationTokenSource(1))
+ {
+ await Assert.ThrowsAnyAsync(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask());
+ }
}
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
- [OuterLoop]
public async Task SendAsync_ReceiveAsync_Connected_Success()
{
using (var receiver = new UdpClient("localhost", 0))
using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
{
await sender.SendAsync(new byte[1], 1);
+ await AssertReceiveAsync(receiver);
+
+ await sender.SendAsync(new ReadOnlyMemory(new byte[1]));
+ await AssertReceiveAsync(receiver);
+
+ await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null);
+ await AssertReceiveAsync(receiver);
+
+ await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null, 0);
+ await AssertReceiveAsync(receiver);
+ }
+ }
+
+ private static async Task AssertReceiveAsync(UdpClient receiver)
+ {
+ UdpReceiveResult result = await receiver.ReceiveAsync();
+ Assert.NotNull(result.RemoteEndPoint);
+ Assert.NotNull(result.Buffer);
+ Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+ public async Task SendAsync_Connected_PreCanceled_Throws()
+ {
+ using (var receiver = new UdpClient("localhost", 0))
+ using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
+ {
+ await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new CancellationToken(true)).AsTask());
+ }
+ }
- UdpReceiveResult result = await receiver.ReceiveAsync();
- Assert.NotNull(result.RemoteEndPoint);
- Assert.NotNull(result.Buffer);
- Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+ public async Task SendAsync_With_HostName_PreCanceled_Throws(bool ipv4)
+ {
+ IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+ using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+ using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+ {
+ await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port, new CancellationToken(true)).AsTask());
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task SendAsync_PreCanceled_Throws(bool ipv4)
+ {
+ IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+ using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+ using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+ {
+ await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port), new CancellationToken(true)).AsTask());
}
}
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs
index 68773696bfcdb..a10e9e4639e71 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs
+++ b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs
@@ -374,7 +374,7 @@ public async Task CloseAsync_DuringConcurrentReceiveAsync_ExpectedStates(Uri ser
[ConditionalFact(nameof(WebSocketsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)]
public async Task CloseAsync_CancelableEvenWhenPendingReceive_Throws()
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs
index 443166cafec66..0fb1314a6068e 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs
+++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs
@@ -58,7 +58,7 @@ public async Task EchoTextMessage_Success(Uri server)
[OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))]
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))]
- [SkipOnPlatform(TestPlatforms.Browser, "CustomHeaders not supported on browser")]
+ [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")]
public async Task ConnectAsync_AddCustomHeaders_Success(Uri server)
{
using (var cws = new ClientWebSocket())
@@ -96,7 +96,7 @@ public async Task ConnectAsync_AddCustomHeaders_Success(Uri server)
[ConditionalFact(nameof(WebSocketsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")]
public async Task ConnectAsync_AddHostHeader_Success()
{
string expectedHost = null;
@@ -218,7 +218,7 @@ public async Task ConnectAsync_PassMultipleSubProtocols_ServerRequires_Connectio
[ConditionalFact(nameof(WebSocketsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on Browser")]
public async Task ConnectAsync_NonStandardRequestHeaders_HeadersAddedWithoutValidation()
{
await LoopbackServer.CreateClientAndServerAsync(async uri =>
@@ -237,7 +237,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri =>
[OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))]
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")]
public async Task ConnectAndCloseAsync_UseProxyServer_ExpectedClosedState(Uri server)
{
using (var cws = new ClientWebSocket())
@@ -282,7 +282,7 @@ public async Task ConnectAsync_CancellationRequestedInflightConnect_ThrowsOperat
[ConditionalFact(nameof(WebSocketsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54152", TestPlatforms.Browser)]
public async Task ConnectAsync_CancellationRequestedAfterConnect_ThrowsOperationCanceledException()
{
var releaseServer = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs
index 1f12d76743357..e0a0e1e59fd84 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs
+++ b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs
@@ -22,7 +22,6 @@ public DeflateTests(ITestOutputHelper output) : base(output)
[ConditionalTheory(nameof(WebSocketsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
[InlineData(15, true, 15, true, "permessage-deflate; client_max_window_bits")]
[InlineData(14, true, 15, true, "permessage-deflate; client_max_window_bits=14")]
[InlineData(15, true, 14, true, "permessage-deflate; client_max_window_bits; server_max_window_bits=14")]
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
index d3a04bdbeef96..bc70d2a4fdf55 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
+++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
@@ -217,6 +217,7 @@ public async Task SendAsync_MultipleOutstandingSendOperations_Throws(Uri server)
[OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))]
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)]
public async Task ReceiveAsync_MultipleOutstandingReceiveOperations_Throws(Uri server)
{
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output))
@@ -320,6 +321,7 @@ await SendAsync(
[OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))]
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)]
public async Task SendReceive_VaryingLengthBuffers_Success(Uri server)
{
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output))
@@ -389,7 +391,7 @@ public async Task SendReceive_Concurrent_Success(Uri server)
[OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))]
[ConditionalFact(nameof(WebSocketsSupported))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)]
public async Task SendReceive_ConnectionClosedPrematurely_ReceiveAsyncFailsAndWebSocketStateUpdated()
{
var options = new LoopbackServer.Options { WebSocketEndpoint = true };
diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
index 22b43cc0edcf9..956ee4d414612 100644
--- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
+++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj
@@ -9,12 +9,17 @@
WasmTestOnBrowser
$(TestArchiveRoot)browseronly/
$(TestArchiveTestsRoot)$(OSPlatformConfig)/
+ $(DefineConstants);TARGET_BROWSER
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
index 7a13a80610d04..40c1e1866688c 100644
--- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
@@ -44,8 +44,8 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int
if (handle.IsInvalid)
{
- handle.Dispose();
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
+ handle.Dispose();
// If we fail to open the file due to a path not existing, we need to know whether to blame
// the file itself or its directory. If we're creating the file, then we blame the directory,
@@ -70,8 +70,9 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int
Interop.Sys.FileStatus status;
if (Interop.Sys.FStat(handle, out status) != 0)
{
+ Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
handle.Dispose();
- throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), path);
+ throw Interop.GetExceptionForIoErrno(error, path);
}
if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR)
{
diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs
index 36710dcdbde5d..5aa7a2c7f4dd6 100644
--- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs
@@ -4,7 +4,8 @@
using System;
using System.Diagnostics;
using System.IO;
-using System.Text;
+using System.IO.Strategies;
+using System.Runtime.InteropServices;
using System.Threading;
namespace Microsoft.Win32.SafeHandles
@@ -24,13 +25,6 @@ public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHand
SetHandle(preexistingHandle);
}
- private SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle, FileOptions fileOptions) : base(ownsHandle)
- {
- SetHandle(preexistingHandle);
-
- _fileOptions = fileOptions;
- }
-
public bool IsAsync => (GetFileOptions() & FileOptions.Asynchronous) != 0;
internal bool CanSeek => !IsClosed && GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_DISK;
@@ -43,10 +37,14 @@ internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileA
{
using (DisableMediaInsertionPrompt.Create())
{
- SafeFileHandle fileHandle = new SafeFileHandle(
- NtCreateFile(fullPath, mode, access, share, options, preallocationSize),
- ownsHandle: true,
- options);
+ // we don't use NtCreateFile as there is no public and reliable way
+ // of converting DOS to NT file paths (RtlDosPathNameToRelativeNtPathName_U_WithStatus is not documented)
+ SafeFileHandle fileHandle = CreateFile(fullPath, mode, access, share, options);
+
+ if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode))
+ {
+ Preallocate(fullPath, preallocationSize, fileHandle);
+ }
fileHandle.InitThreadPoolBindingIfNeeded();
@@ -54,48 +52,91 @@ internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileA
}
}
- private static IntPtr NtCreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ private static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
{
- uint ntStatus;
- IntPtr fileHandle;
+ Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default;
+ if ((share & FileShare.Inheritable) != 0)
+ {
+ secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES
+ {
+ nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES),
+ bInheritHandle = Interop.BOOL.TRUE
+ };
+ }
- const string MandatoryNtPrefix = @"\??\";
- if (fullPath.StartsWith(MandatoryNtPrefix, StringComparison.Ordinal))
+ int fAccess =
+ ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) |
+ ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0);
+
+ // Our Inheritable bit was stolen from Windows, but should be set in
+ // the security attributes class. Don't leave this bit set.
+ share &= ~FileShare.Inheritable;
+
+ // Must use a valid Win32 constant here...
+ if (mode == FileMode.Append)
{
- (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(fullPath, mode, access, share, options, preallocationSize);
+ mode = FileMode.OpenOrCreate;
}
- else
+
+ int flagsAndAttributes = (int)options;
+
+ // For mitigating local elevation of privilege attack through named pipes
+ // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
+ // named pipe server can't impersonate a high privileged client security context
+ // (note that this is the effective default on CreateFile2)
+ flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
+
+ SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(fullPath, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
+ if (fileHandle.IsInvalid)
{
- var vsb = new ValueStringBuilder(stackalloc char[256]);
- vsb.Append(MandatoryNtPrefix);
+ // Return a meaningful exception with the full path.
- if (fullPath.StartsWith(@"\\?\", StringComparison.Ordinal)) // NtCreateFile does not support "\\?\" prefix, only "\??\"
- {
- vsb.Append(fullPath.AsSpan(4));
- }
- else
+ // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
+ // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
+ // probably be consistent w/ every other directory.
+ int errorCode = Marshal.GetLastPInvokeError();
+
+ if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && fullPath!.Length == PathInternal.GetRootLength(fullPath))
{
- vsb.Append(fullPath);
+ errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
}
- (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(vsb.AsSpan(), mode, access, share, options, preallocationSize);
- vsb.Dispose();
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
}
- switch (ntStatus)
- {
- case Interop.StatusOptions.STATUS_SUCCESS:
- return fileHandle;
- case Interop.StatusOptions.STATUS_DISK_FULL:
- throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize));
- // NtCreateFile has a bug and it reports STATUS_INVALID_PARAMETER for files
- // that are too big for the current file system. Example: creating a 4GB+1 file on a FAT32 drive.
- case Interop.StatusOptions.STATUS_INVALID_PARAMETER when preallocationSize > 0:
- case Interop.StatusOptions.STATUS_FILE_TOO_LARGE:
- throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize));
- default:
- int error = (int)Interop.NtDll.RtlNtStatusToDosError((int)ntStatus);
- throw Win32Marshal.GetExceptionForWin32Error(error, fullPath);
+ fileHandle._fileOptions = options;
+ return fileHandle;
+ }
+
+ private static unsafe void Preallocate(string fullPath, long preallocationSize, SafeFileHandle fileHandle)
+ {
+ var allocationInfo = new Interop.Kernel32.FILE_ALLOCATION_INFO
+ {
+ AllocationSize = preallocationSize
+ };
+
+ if (!Interop.Kernel32.SetFileInformationByHandle(
+ fileHandle,
+ Interop.Kernel32.FileAllocationInfo,
+ &allocationInfo,
+ (uint)sizeof(Interop.Kernel32.FILE_ALLOCATION_INFO)))
+ {
+ int errorCode = Marshal.GetLastPInvokeError();
+
+ // we try to mimic the atomic NtCreateFile here:
+ // if preallocation fails, close the handle and delete the file
+ fileHandle.Dispose();
+ Interop.Kernel32.DeleteFile(fullPath);
+
+ switch (errorCode)
+ {
+ case Interop.Errors.ERROR_DISK_FULL:
+ throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize));
+ case Interop.Errors.ERROR_FILE_TOO_LARGE:
+ throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize));
+ default:
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
+ }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 99b032174387f..7c80c23f63241 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -813,6 +813,9 @@
The elements of the AdjustmentRule array must be in chronological order and must not overlap.
+
+ The alignment must be a power of two.
+
The object already has a CCW associated with it.
@@ -3784,4 +3787,4 @@
A MemberInfo that matches '{0}' could not be found.
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index a189be9ac1517..1186b2c606f63 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -824,6 +824,7 @@
+
@@ -1068,6 +1069,7 @@
+
@@ -1425,6 +1427,9 @@
Common\Interop\Windows\Kernel32\Interop.FILE_BASIC_INFO.cs
+
+ Common\Interop\Windows\Kernel32\Interop.FILE_ALLOCATION_INFO.cs
+
Common\Interop\Windows\Kernel32\Interop.FILE_END_OF_FILE_INFO.cs
@@ -1611,6 +1616,9 @@
Common\Interop\Windows\Interop.UNICODE_STRING.cs
+
+ Common\Interop\Windows\Kernel32\Interop.SecurityOptions.cs
+
Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs
@@ -1725,6 +1733,9 @@
Common\Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs
+
+ Common\Interop\Windows\Ucrtbase\Interop.MemAlloc.cs
+
Common\Interop\Windows\User32\Interop.Constants.cs
@@ -1796,6 +1807,7 @@
+
@@ -2067,6 +2079,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
index 321d60de739b3..d6093b4fb66bb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
@@ -495,6 +495,9 @@ public static void SendCommand(EventSource eventSource, EventCommand command, ID
///
public override string ToString()
{
+ if (!IsSupported)
+ return base.ToString()!;
+
return SR.Format(SR.EventSource_ToString, Name, Guid);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs
index 22e990194f5d8..3b90d71120eda 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs
@@ -20,12 +20,13 @@ private unsafe bool GetData()
{
Debug.Assert(_directoryHandle != (IntPtr)(-1) && _directoryHandle != IntPtr.Zero && !_lastEntryFound);
+ Interop.NtDll.IO_STATUS_BLOCK statusBlock;
int status = Interop.NtDll.NtQueryDirectoryFile(
FileHandle: _directoryHandle,
Event: IntPtr.Zero,
ApcRoutine: IntPtr.Zero,
ApcContext: IntPtr.Zero,
- IoStatusBlock: out Interop.NtDll.IO_STATUS_BLOCK statusBlock,
+ IoStatusBlock: &statusBlock,
FileInformation: _buffer,
Length: (uint)_bufferLength,
FileInformationClass: Interop.NtDll.FILE_INFORMATION_CLASS.FileFullDirectoryInformation,
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index ecc990b07c6e1..5a7f0c8d1bbe5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -129,17 +129,5 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path)
return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan.Empty;
}
- /// Gets whether the system is case-sensitive.
- internal static bool IsCaseSensitive
- {
- get
- {
- #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
- return false;
- #else
- return true;
- #endif
- }
- }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index 5e641806926b8..5f4b8e11a1920 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -230,9 +230,6 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path)
return pathRoot <= 0 ? ReadOnlySpan.Empty : path.Slice(0, pathRoot);
}
- /// Gets whether the system is case-sensitive.
- internal static bool IsCaseSensitive => false;
-
///
/// Returns the volume name for dos, UNC and device paths.
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
index 7601e45e074d1..104704de7b715 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
@@ -860,7 +860,7 @@ private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int by
/// Thrown if or is null or an empty string.
public static string GetRelativePath(string relativeTo, string path)
{
- return GetRelativePath(relativeTo, path, StringComparison);
+ return GetRelativePath(relativeTo, path, PathInternal.StringComparison);
}
private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
@@ -957,12 +957,6 @@ private static string GetRelativePath(string relativeTo, string path, StringComp
return sb.ToString();
}
- /// Returns a comparison that can be used to compare file and directory names for equality.
- internal static StringComparison StringComparison =>
- IsCaseSensitive ?
- StringComparison.Ordinal :
- StringComparison.OrdinalIgnoreCase;
-
///
/// Trims one trailing directory separator beyond the root of the path.
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs
index 49fbb26ff4ca4..b5ffd5e777d98 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs
@@ -72,9 +72,21 @@ public static class BitOperations
public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0;
///
- /// Round the given integral value up to a power of 2.
+ /// Evaluate whether a given integral value is a power of 2.
///
/// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsPow2(nint value) => (value & (value - 1)) == 0 && value > 0;
+
+ ///
+ /// Evaluate whether a given integral value is a power of 2.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsPow2(nuint value) => (value & (value - 1)) == 0 && value != 0;
+
+ /// Round the given integral value up to a power of 2.
+ /// The value.
///
/// The smallest power of 2 which is greater than or equal to .
/// If is 0 or the result overflows, returns 0.
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs
index 0853bfff0685a..4291dded86107 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs
@@ -126,7 +126,7 @@ public object Clone()
_publicKey = (byte[]?)_publicKey?.Clone(),
_publicKeyToken = (byte[]?)_publicKeyToken?.Clone(),
_cultureInfo = _cultureInfo,
- _version = (Version?)_version?.Clone(),
+ _version = _version,
_flags = _flags,
_codeBase = _codeBase,
_hashAlgorithm = _hashAlgorithm,
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs
index 08ab87a390fff..49ef8223c370f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs
@@ -62,73 +62,37 @@ internal static unsafe void GetAnsiStringBytes(ReadOnlySpan chars, Span AllocHGlobal((nint)(uint)cb);
public static void FreeCoTaskMem(IntPtr ptr) => FreeHGlobal(ptr);
- public static IntPtr ReAllocCoTaskMem(IntPtr pv, int cb)
+ public static unsafe IntPtr ReAllocCoTaskMem(IntPtr pv, int cb)
{
nuint cbNative = (nuint)(uint)cb;
+ void* pvNative = (void*)(nint)pv;
- if (cbNative == 0)
+ if ((cbNative == 0) && (pvNative != null))
{
- if (pv != IntPtr.Zero)
- {
- Interop.Sys.MemFree(pv);
- return IntPtr.Zero;
- }
- // Avoid undefined realloc behavior by always allocating at least one byte
- cbNative = 1;
+ Interop.Sys.Free(pvNative);
+ return IntPtr.Zero;
}
- IntPtr pNewMem = Interop.Sys.MemReAlloc(pv, cbNative);
- if (pNewMem == IntPtr.Zero)
- {
- throw new OutOfMemoryException();
- }
- return pNewMem;
+ return (nint)NativeMemory.Realloc((void*)(nint)pv, cbNative);
}
internal static unsafe IntPtr AllocBSTR(int length)
@@ -137,22 +101,24 @@ internal static unsafe IntPtr AllocBSTR(int length)
const nuint WIN32_ALLOC_ALIGN = 15;
ulong cbNative = 2 * (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN;
+
if (cbNative > uint.MaxValue)
{
throw new OutOfMemoryException();
}
- IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
- if (p == IntPtr.Zero)
+ void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
+
+ if (p == null)
{
throw new OutOfMemoryException();
}
- IntPtr s = p + sizeof(IntPtr);
+ void* s = (byte*)p + sizeof(nuint);
*(((uint*)s) - 1) = (uint)(length * sizeof(char));
((char*)s)[length] = '\0';
- return s;
+ return (nint)s;
}
internal static unsafe IntPtr AllocBSTRByteLen(uint length)
@@ -161,32 +127,36 @@ internal static unsafe IntPtr AllocBSTRByteLen(uint length)
const nuint WIN32_ALLOC_ALIGN = 15;
ulong cbNative = (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN;
+
if (cbNative > uint.MaxValue)
{
throw new OutOfMemoryException();
}
- IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
- if (p == IntPtr.Zero)
+ void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
+
+ if (p == null)
{
throw new OutOfMemoryException();
}
- IntPtr s = p + sizeof(IntPtr);
+ void* s = (byte*)p + sizeof(nuint);
*(((uint*)s) - 1) = (uint)length;
// NULL-terminate with both a narrow and wide zero.
*(byte*)((byte*)s + length) = (byte)'\0';
*(short*)((byte*)s + ((length + 1) & ~1)) = 0;
- return s;
+ return (nint)s;
}
public static unsafe void FreeBSTR(IntPtr ptr)
{
- if (ptr != IntPtr.Zero)
+ void* ptrNative = (void*)(nint)ptr;
+
+ if (ptrNative != null)
{
- Interop.Sys.MemFree(ptr - sizeof(IntPtr));
+ Interop.Sys.Free((byte*)ptr - sizeof(nuint));
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs
new file mode 100644
index 0000000000000..4dc1f1b7f5aa5
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs
@@ -0,0 +1,210 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+ /// This class contains methods that are mainly used to manage native memory.
+ public static unsafe partial class NativeMemory
+ {
+ /// Allocates an aligned block of memory of the specified size and alignment, in bytes.
+ /// The size, in bytes, of the block to allocate.
+ /// The alignment, in bytes, of the block to allocate. This must be a power of 2.
+ /// A pointer to the allocated aligned block of memory.
+ /// is not a power of two.
+ /// Allocating of memory with failed.
+ ///
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C aligned_alloc API or a platform dependent aligned allocation API such as _aligned_malloc on Win32.
+ /// This method is not compatible with or , instead or should be called.
+ ///
+ [CLSCompliant(false)]
+ public static void* AlignedAlloc(nuint byteCount, nuint alignment)
+ {
+ if (!BitOperations.IsPow2(alignment))
+ {
+ // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero
+ // POSIX additionally requires alignment to be at least sizeof(void*)
+
+ // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the
+ // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these
+ // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow
+ // occurs we will get a result that is less than alignment which will cause the allocation to fail.
+ //
+ // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to
+ // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size.
+
+ nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*));
+ nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment;
+
+ void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedAlloc(adjustedAlignment, adjustedByteCount);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Frees an aligned block of memory.
+ /// A pointer to the aligned block of memory that should be freed.
+ ///
+ /// This method does nothing if is null.
+ /// This method is a thin wrapper over the C free API or a platform dependent aligned free API such as _aligned_free on Win32.
+ ///
+ [CLSCompliant(false)]
+ public static void AlignedFree(void* ptr)
+ {
+ if (ptr != null)
+ {
+ Interop.Sys.AlignedFree(ptr);
+ }
+ }
+
+ /// Reallocates an aligned block of memory of the specified size and alignment, in bytes.
+ /// The previously allocated block of memory.
+ /// The size, in bytes, of the block to allocate.
+ /// The alignment, in bytes, of the block to allocate. This must be a power of 2.
+ /// A pointer to the reallocated aligned block of memory.
+ /// is not a power of two.
+ /// Reallocating of memory with failed.
+ ///
+ /// This method acts as if is null.
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a platform dependent aligned reallocation API such as _aligned_realloc on Win32.
+ /// This method is not compatible with or , instead or should be called.
+ ///
+ [CLSCompliant(false)]
+ public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment)
+ {
+ if (!BitOperations.IsPow2(alignment))
+ {
+ // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero
+ // POSIX additionally requires alignment to be at least sizeof(void*)
+
+ // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the
+ // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these
+ // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow
+ // occurs we will get a result that is less than alignment which will cause the allocation to fail.
+ //
+ // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to
+ // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size.
+
+ nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*));
+ nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment;
+
+ void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedRealloc(ptr, adjustedAlignment, adjustedByteCount);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Allocates a block of memory of the specified size, in bytes.
+ /// The size, in bytes, of the block to allocate.
+ /// A pointer to the allocated block of memory.
+ /// Allocating of memory failed.
+ ///
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C malloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* Alloc(nuint byteCount)
+ {
+ // The C standard does not define what happens when size == 0, we want an "empty" allocation
+ void* result = Interop.Sys.Malloc((byteCount != 0) ? byteCount : 1);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Allocates and zeroes a block of memory of the specified size, in elements.
+ /// The count, in elements, of the block to allocate.
+ /// The size, in bytes, of each element in the allocation.
+ /// A pointer to the allocated and zeroed block of memory.
+ /// Allocating * bytes of memory failed.
+ ///
+ /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C calloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* AllocZeroed(nuint elementCount, nuint elementSize)
+ {
+ void* result = null;
+
+ if ((elementCount != 0) && (elementSize != 0))
+ {
+ result = Interop.Sys.Calloc(elementCount, elementSize);
+ }
+ else
+ {
+ // The C standard does not define what happens when num == 0 or size == 0, we want an "empty" allocation
+ result = Interop.Sys.Malloc(1);
+ }
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Frees a block of memory.
+ /// A pointer to the block of memory that should be freed.
+ ///
+ /// This method does nothing if is null.
+ /// This method is a thin wrapper over the C free API.
+ ///
+ [CLSCompliant(false)]
+ public static void Free(void* ptr)
+ {
+ if (ptr != null)
+ {
+ Interop.Sys.Free(ptr);
+ }
+ }
+
+ /// Reallocates a block of memory to be the specified size, in bytes.
+ /// The previously allocated block of memory.
+ /// The size, in bytes, of the reallocated block.
+ /// A pointer to the reallocated block of memory.
+ /// Reallocating of memory failed.
+ ///
+ /// This method acts as if is null.
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C realloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* Realloc(void* ptr, nuint byteCount)
+ {
+ // The C standard does not define what happens when size == 0, we want an "empty" allocation
+ void* result = Interop.Sys.Realloc(ptr, (byteCount != 0) ? byteCount : 1);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs
new file mode 100644
index 0000000000000..7fbaf933ef924
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs
@@ -0,0 +1,175 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+ /// This class contains methods that are mainly used to manage native memory.
+ public static unsafe partial class NativeMemory
+ {
+ /// Allocates an aligned block of memory of the specified size and alignment, in bytes.
+ /// The size, in bytes, of the block to allocate.
+ /// The alignment, in bytes, of the block to allocate. This must be a power of 2.
+ /// A pointer to the allocated aligned block of memory.
+ /// is not a power of two.
+ /// Allocating of memory with failed.
+ ///
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C aligned_alloc API or a platform dependent aligned allocation API such as _aligned_malloc on Win32.
+ /// This method is not compatible with or , instead or should be called.
+ ///
+ [CLSCompliant(false)]
+ public static void* AlignedAlloc(nuint byteCount, nuint alignment)
+ {
+ if (!BitOperations.IsPow2(alignment))
+ {
+ // The C standard doesn't define what a valid alignment is, however Windows and POSIX The Windows implementation requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero
+ void* result = Interop.Ucrtbase._aligned_malloc((byteCount != 0) ? byteCount : 1, alignment);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Frees an aligned block of memory.
+ /// A pointer to the aligned block of memory that should be freed.
+ ///
+ /// This method does nothing if is null.
+ /// This method is a thin wrapper over the C free API or a platform dependent aligned free API such as _aligned_free on Win32.
+ ///
+ [CLSCompliant(false)]
+ public static void AlignedFree(void* ptr)
+ {
+ if (ptr != null)
+ {
+ Interop.Ucrtbase._aligned_free(ptr);
+ }
+ }
+
+ /// Reallocates an aligned block of memory of the specified size and alignment, in bytes.
+ /// The previously allocated block of memory.
+ /// The size, in bytes, of the block to allocate.
+ /// The alignment, in bytes, of the block to allocate. This must be a power of 2.
+ /// A pointer to the reallocated aligned block of memory.
+ /// is not a power of two.
+ /// Reallocating of memory with failed.
+ ///
+ /// This method acts as if is null.
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a platform dependent aligned reallocation API such as _aligned_realloc on Win32.
+ /// This method is not compatible with or , instead or should be called.
+ ///
+ [CLSCompliant(false)]
+ public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment)
+ {
+ if (!BitOperations.IsPow2(alignment))
+ {
+ // The C standard doesn't define what a valid alignment is, however Windows and POSIX The Windows implementation requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero
+ void* result = Interop.Ucrtbase._aligned_realloc(ptr, (byteCount != 0) ? byteCount : 1, alignment);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Allocates a block of memory of the specified size, in bytes.
+ /// The size, in bytes, of the block to allocate.
+ /// A pointer to the allocated block of memory.
+ /// Allocating of memory failed.
+ ///
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C malloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* Alloc(nuint byteCount)
+ {
+ // The Windows implementation handles size == 0 as we expect
+ void* result = Interop.Ucrtbase.malloc(byteCount);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Allocates and zeroes a block of memory of the specified size, in elements.
+ /// The count, in elements, of the block to allocate.
+ /// The size, in bytes, of each element in the allocation.
+ /// A pointer to the allocated and zeroed block of memory.
+ /// Allocating * bytes of memory failed.
+ ///
+ /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C calloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* AllocZeroed(nuint elementCount, nuint elementSize)
+ {
+ // The Windows implementation handles num == 0 && size == 0 as we expect
+ void* result = Interop.Ucrtbase.calloc(elementCount, elementSize);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+
+ /// Frees a block of memory.
+ /// A pointer to the block of memory that should be freed.
+ ///
+ /// This method does nothing if is null.
+ /// This method is a thin wrapper over the C free API.
+ ///
+ [CLSCompliant(false)]
+ public static void Free(void* ptr)
+ {
+ if (ptr != null)
+ {
+ Interop.Ucrtbase.free(ptr);
+ }
+ }
+
+ /// Reallocates a block of memory to be the specified size, in bytes.
+ /// The previously allocated block of memory.
+ /// The size, in bytes, of the reallocated block.
+ /// A pointer to the reallocated block of memory.
+ /// Reallocating of memory failed.
+ ///
+ /// This method acts as if is null.
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C realloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* Realloc(void* ptr, nuint byteCount)
+ {
+ // The Windows implementation treats size == 0 as Free, we want an "empty" allocation
+ void* result = Interop.Ucrtbase.realloc(ptr, (byteCount != 0) ? byteCount : 1);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs
new file mode 100644
index 0000000000000..b46655ec58c00
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+ public static unsafe partial class NativeMemory
+ {
+ /// Allocates a block of memory of the specified size, in elements.
+ /// The count, in elements, of the block to allocate.
+ /// The size, in bytes, of each element in the allocation.
+ /// A pointer to the allocated block of memory.
+ /// Allocating * bytes of memory failed.
+ ///
+ /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C malloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* Alloc(nuint elementCount, nuint elementSize)
+ {
+ nuint byteCount = GetByteCount(elementCount, elementSize);
+ return Alloc(byteCount);
+ }
+
+ /// Allocates and zeroes a block of memory of the specified size, in bytes.
+ /// The size, in bytes, of the block to allocate.
+ /// A pointer to the allocated and zeroed block of memory.
+ /// Allocating of memory failed.
+ ///
+ /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.
+ /// This method is a thin wrapper over the C calloc API.
+ ///
+ [CLSCompliant(false)]
+ public static void* AllocZeroed(nuint byteCount)
+ {
+ return AllocZeroed(byteCount, elementSize: 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static nuint GetByteCount(nuint elementCount, nuint elementSize)
+ {
+ // This is based on the `mi_count_size_overflow` and `mi_mul_overflow` methods from microsoft/mimalloc.
+ // Original source is Copyright (c) 2019 Microsoft Corporation, Daan Leijen. Licensed under the MIT license
+
+ // sqrt(nuint.MaxValue)
+ nuint multiplyNoOverflow = (nuint)1 << (4 * sizeof(nuint));
+
+ return ((elementSize >= multiplyNoOverflow) || (elementCount >= multiplyNoOverflow)) && (elementSize > 0) && ((nuint.MaxValue / elementSize) < elementCount) ? nuint.MaxValue : (elementCount * elementSize);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
index 6c733ef6ca150..2a5a3c1da07fb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
@@ -781,7 +781,7 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly)
string assemblyPath = Path.Combine(parentDirectory, assemblyName.CultureName!, $"{assemblyName.Name}.dll");
bool exists = System.IO.FileSystem.FileExists(assemblyPath);
- if (!exists && Path.IsCaseSensitive)
+ if (!exists && PathInternal.IsCaseSensitive)
{
#if CORECLR
if (AssemblyLoadContext.IsTracingEnabled())
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs
index 7c4a2da7babb1..d5d1952e7a150 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs
@@ -17,7 +17,7 @@ private static unsafe bool CheckForAvailableMemory(out ulong availPageFile, out
{
Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default;
memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX);
- if (!Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus))
+ if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) == Interop.BOOL.FALSE)
{
availPageFile = default;
totalAddressSpaceFree = default;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs
new file mode 100644
index 0000000000000..b16c4d067b7ed
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs
@@ -0,0 +1,220 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Runtime.ExceptionServices;
+using System.Threading.Tasks;
+using System.Threading.Tasks.Sources;
+
+namespace System.Threading
+{
+ /// Provides a periodic timer that enables waiting asynchronously for timer ticks.
+ ///
+ /// This timer is intended to be used only by a single consumer at a time: only one call to
+ /// may be in flight at any given moment. may be used concurrently with an active
+ /// to interrupt it and cause it to return false.
+ ///
+ public sealed class PeriodicTimer : IDisposable
+ {
+ /// The underlying timer.
+ private readonly TimerQueueTimer _timer;
+ /// All state other than the _timer, so that the rooted timer's callback doesn't indirectly root itself by referring to _timer.
+ private readonly State _state;
+
+ /// Initializes the timer.
+ /// The time interval between invocations of callback..
+ /// must be represent a number of milliseconds larger than 0 and smaller than .
+ public PeriodicTimer(TimeSpan period)
+ {
+ long ms = (long)period.TotalMilliseconds;
+ if (ms < 1 || ms > Timer.MaxSupportedTimeout)
+ {
+ GC.SuppressFinalize(this);
+ throw new ArgumentOutOfRangeException(nameof(period));
+ }
+
+ _state = new State();
+ _timer = new TimerQueueTimer(s => ((State)s!).Signal(), _state, (uint)ms, (uint)ms, flowExecutionContext: false);
+ }
+
+ /// Wait for the next tick of the timer, or for the timer to be stopped.
+ ///
+ /// A to use to cancel the asynchronous wait. If cancellation is requested, it affects only the single wait operation;
+ /// the underlying timer continues firing.
+ ///
+ /// A task that will be completed due to the timer firing, being called to stop the timer, or cancellation being requested.
+ ///
+ /// The behaves like an auto-reset event, in that multiple ticks are coalesced into a single tick if they occur between
+ /// calls to . Similarly, a call to will void any tick not yet consumed.
+ /// may only be used by one consumer at a time, and may be used concurrently with a single call to .
+ ///
+ public ValueTask WaitForNextTickAsync(CancellationToken cancellationToken = default) =>
+ _state.WaitForNextTickAsync(this, cancellationToken);
+
+ /// Stops the timer and releases associated managed resources.
+ ///
+ /// will cause an active wait with to complete with a value of false.
+ /// All subsequent invocations will produce a value of false.
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ _timer.Close();
+ _state.Signal(stopping: true);
+ }
+
+ ~PeriodicTimer() => Dispose();
+
+ /// Core implementation for the periodic timer.
+ private sealed class State : IValueTaskSource
+ {
+ /// The associated .
+ ///
+ /// This should refer to the parent instance only when there's an active waiter, and be null when there
+ /// isn't. The TimerQueueTimer in the PeriodicTimer strongly roots itself, and it references this State
+ /// object:
+ /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --ref--> null
+ /// If this State object then references the PeriodicTimer, it creates a strongly-rooted cycle that prevents anything from
+ /// being GC'd:
+ /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --v
+ /// ^--ref-------------------------------------------------------------------|
+ /// When this field is null, the cycle is broken, and dropping all references to the PeriodicTimer allows the
+ /// PeriodicTimer to be finalized and unroot the TimerQueueTimer. Thus, we keep this field set during
+ /// so that the timer roots any async continuation chain awaiting it, and then keep it unset otherwise so that everything
+ /// can be GC'd appropriately.
+ ///
+ private PeriodicTimer? _owner;
+ /// Core of the implementation.
+ private ManualResetValueTaskSourceCore _mrvtsc;
+ /// Cancellation registration for any active call.
+ private CancellationTokenRegistration _ctr;
+ /// Whether the timer has been stopped.
+ private bool _stopped;
+ /// Whether there's a pending notification to be received. This could be due to the timer firing, the timer being stopped, or cancellation being requested.
+ private bool _signaled;
+ /// Whether there's a call in flight.
+ private bool _activeWait;
+
+ /// Wait for the next tick of the timer, or for the timer to be stopped.
+ public ValueTask WaitForNextTickAsync(PeriodicTimer owner, CancellationToken cancellationToken)
+ {
+ lock (this)
+ {
+ if (_activeWait)
+ {
+ // WaitForNextTickAsync should only be used by one consumer at a time. Failing to do so is an error.
+ ThrowHelper.ThrowInvalidOperationException();
+ }
+
+ // If cancellation has already been requested, short-circuit.
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return ValueTask.FromCanceled(cancellationToken);
+ }
+
+ // If the timer has a pending tick or has been stopped, we can complete synchronously.
+ if (_signaled)
+ {
+ // Reset the signal for subsequent consumers, but only if we're not stopped. Since.
+ // stopping the timer is one way, any subsequent calls should also complete synchronously
+ // with false, and thus we leave _signaled pinned at true.
+ if (!_stopped)
+ {
+ _signaled = false;
+ }
+
+ return new ValueTask(!_stopped);
+ }
+
+ Debug.Assert(!_stopped, "Unexpectedly stopped without _signaled being true.");
+
+ // Set up for the wait and return a task that will be signaled when the
+ // timer fires, stop is called, or cancellation is requested.
+ _owner = owner;
+ _activeWait = true;
+ _ctr = cancellationToken.UnsafeRegister(static (state, cancellationToken) => ((State)state!).Signal(cancellationToken: cancellationToken), this);
+
+ return new ValueTask(this, _mrvtsc.Version);
+ }
+ }
+
+ /// Signal that the timer has either fired or been stopped.
+ public void Signal(bool stopping = false, CancellationToken cancellationToken = default)
+ {
+ bool completeTask = false;
+
+ lock (this)
+ {
+ _stopped |= stopping;
+ if (!_signaled)
+ {
+ _signaled = true;
+ completeTask = _activeWait;
+ }
+ }
+
+ if (completeTask)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ // If cancellation is requested just before the UnsafeRegister call, it's possible this will end up being invoked
+ // as part of the WaitForNextTickAsync call and thus as part of holding the lock. The goal of completeTask
+ // was to escape that lock, so that we don't invoke any synchronous continuations from the ValueTask as part
+ // of completing _mrvtsc. However, in that case, we also haven't returned the ValueTask to the caller, so there
+ // won't be any continuations yet, which makes this safe.
+ _mrvtsc.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException(cancellationToken)));
+ }
+ else
+ {
+ Debug.Assert(!Monitor.IsEntered(this));
+ _mrvtsc.SetResult(true);
+ }
+ }
+ }
+
+ ///
+ bool IValueTaskSource.GetResult(short token)
+ {
+ // Dispose of the cancellation registration. This is done outside of the below lock in order
+ // to avoid a potential deadlock due to waiting for a concurrent cancellation callback that might
+ // in turn try to take the lock. For valid usage, GetResult is only called once _ctr has been
+ // successfully initialized before WaitForNextTickAsync returns to its synchronous caller, and
+ // there should be no race conditions accessing it, as concurrent consumption is invalid. If there
+ // is invalid usage, with GetResult used erroneously/concurrently, the worst that happens is cancellation
+ // may not take effect for the in-flight operation, with its registration erroneously disposed.
+ // Note we use Dispose rather than Unregister (which wouldn't risk deadlock) so that we know that thecancellation callback associated with this operation
+ // won't potentially still fire after we've completed this GetResult and a new operation
+ // has potentially started.
+ _ctr.Dispose();
+
+ lock (this)
+ {
+ try
+ {
+ _mrvtsc.GetResult(token);
+ }
+ finally
+ {
+ _mrvtsc.Reset();
+ _ctr = default;
+ _activeWait = false;
+ _owner = null;
+ if (!_stopped)
+ {
+ _signaled = false;
+ }
+ }
+
+ return !_stopped;
+ }
+ }
+
+ ///
+ ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _mrvtsc.GetStatus(token);
+
+ ///
+ void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) =>
+ _mrvtsc.OnCompleted(continuation, state, token, flags);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
index 94b2e6d0b18d7..9a5ef787d7c78 100644
--- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
@@ -975,6 +975,8 @@ private static string GetResourceString(ExceptionResource resource)
return SR.Argument_InvalidFlag;
case ExceptionResource.CancellationTokenSource_Disposed:
return SR.CancellationTokenSource_Disposed;
+ case ExceptionResource.Argument_AlignmentMustBePow2:
+ return SR.Argument_AlignmentMustBePow2;
default:
Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum.");
return "";
@@ -1158,5 +1160,6 @@ internal enum ExceptionResource
Argument_SpansMustHaveSameLength,
Argument_InvalidFlag,
CancellationTokenSource_Disposed,
+ Argument_AlignmentMustBePow2,
}
}
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs
index 294b174638b79..a42e0fef36c26 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs
@@ -2015,12 +2015,12 @@ private void ProcessReaderEvent()
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
- _validator.ValidateWhitespace(GetStringValue);
+ _validator.ValidateWhitespace(_valueGetter);
break;
case XmlNodeType.Text: // text inside a node
case XmlNodeType.CDATA: //
- _validator.ValidateText(GetStringValue);
+ _validator.ValidateText(_valueGetter);
break;
case XmlNodeType.EndElement:
@@ -2543,12 +2543,12 @@ private object InternalReadContentAsObject(bool unwrapTypedValue, out string ori
case XmlNodeType.Text:
case XmlNodeType.CDATA:
- _validator.ValidateText(GetStringValue);
+ _validator.ValidateText(_valueGetter);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
- _validator.ValidateWhitespace(GetStringValue);
+ _validator.ValidateWhitespace(_valueGetter);
break;
case XmlNodeType.Comment:
@@ -2611,12 +2611,12 @@ private void ReadAheadForMemberType()
case XmlNodeType.Text:
case XmlNodeType.CDATA:
- _validator.ValidateText(GetStringValue);
+ _validator.ValidateText(_valueGetter);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
- _validator.ValidateWhitespace(GetStringValue);
+ _validator.ValidateWhitespace(_valueGetter);
break;
case XmlNodeType.Comment:
@@ -2672,12 +2672,12 @@ private void GetIsDefault()
case XmlNodeType.Text:
case XmlNodeType.CDATA:
- _validator.ValidateText(GetStringValue);
+ _validator.ValidateText(_valueGetter);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
- _validator.ValidateWhitespace(GetStringValue);
+ _validator.ValidateWhitespace(_valueGetter);
break;
case XmlNodeType.Comment:
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs
index c7860253a5858..aa60c108b5c90 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs
@@ -42,7 +42,8 @@ internal sealed class ValidationState
public bool HasMatched; // whether the element has been verified correctly
//For NFAs
- public BitSet[] CurPos = new BitSet[2];
+ private BitSet[]? _curPos;
+ public BitSet[] CurPos => _curPos ??= new BitSet[2];
//For all
public BitSet? AllElementsSet;
diff --git a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj
index e2a0ebdd80382..0c059eae7c103 100644
--- a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj
+++ b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj
@@ -51,6 +51,7 @@
+
@@ -85,4 +86,4 @@
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs
index 189a9460d32c5..acb8bb0f42290 100644
--- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs
+++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs
@@ -16,7 +16,7 @@ static unsafe MemoryMonitor()
{
Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default;
memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX);
- if (Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus))
+ if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) != Interop.BOOL.FALSE)
{
s_totalPhysical = (long)memoryStatus.ullTotalPhys;
s_totalVirtual = (long)memoryStatus.ullTotalVirtual;
diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs
index 078bf9ed3794e..6aff99b66b19d 100644
--- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs
+++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs
@@ -14,7 +14,7 @@ protected override unsafe int GetCurrentPressure()
{
Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default;
memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX);
- if (!Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus))
+ if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) == Interop.BOOL.FALSE)
{
return 0;
}
diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
index 0cf9affe8da76..02e739ad73173 100644
--- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
+++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
@@ -760,6 +760,27 @@ public static void SetDllImportResolver(System.Reflection.Assembly assembly, Sys
public static bool TryLoad(string libraryPath, out System.IntPtr handle) { throw null; }
public static bool TryLoad(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath, out System.IntPtr handle) { throw null; }
}
+ public static unsafe partial class NativeMemory
+ {
+ [System.CLSCompliantAttribute(false)]
+ public static void* AlignedAlloc(nuint byteCount, nuint alignment) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void AlignedFree(void* ptr) { }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Alloc(nuint byteCount) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Alloc(nuint elementCount, nuint elementSize) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AllocZeroed(nuint byteCount) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AllocZeroed(nuint elementCount, nuint elementSize) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void Free(void* ptr) { }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Realloc(void* ptr, nuint byteCount) { throw null; }
+ }
public readonly struct NFloat : IEquatable
{
public NFloat(float value) { }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
index e04dab6046036..9ed8c6ff8a776 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs
new file mode 100644
index 0000000000000..9787751d87ae2
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs
@@ -0,0 +1,439 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Runtime.InteropServices.Tests
+{
+ public unsafe class NativeMemoryTests
+ {
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ [InlineData(4)]
+ [InlineData(8)]
+ [InlineData(16)]
+ [InlineData(32)]
+ [InlineData(64)]
+ [InlineData(128)]
+ [InlineData(256)]
+ [InlineData(512)]
+ [InlineData(1 * 1024)]
+ [InlineData(2 * 1024)]
+ [InlineData(4 * 1024)]
+ [InlineData(8 * 1024)]
+ [InlineData(16 * 1024)]
+ [InlineData(64 * 1024)]
+ [InlineData(1 * 1024 * 1024)]
+ [InlineData(2 * 1024 * 1024)]
+ [InlineData(4 * 1024 * 1024)]
+ public void AlignedAllocTest(uint alignment)
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, alignment);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % alignment == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedAllocLessThanVoidPtrAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, 1);
+ Assert.True(ptr != null);
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedAllocOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.AlignedAlloc(nuint.MaxValue - ((uint)sizeof(nuint) - 1), (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedAllocZeroAlignmentTest()
+ {
+ Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), 0));
+ }
+
+ [Fact]
+ public void AlignedAllocNonPowerOfTwoAlignmentTest()
+ {
+ Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) + 1));
+ Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) * 3));
+ }
+
+ [Fact]
+ public void AlignedAllocOverflowByteCountTest()
+ {
+ // POSIX requires byteCount to be a multiple of alignment and so we will internally upsize.
+ // This upsizing can overflow for certain values since we do (byteCount + (alignment - 1)) & ~(alignment - 1)
+ //
+ // However, this overflow is "harmless" since it will result in a value that is less than alignment
+ // given that alignment is a power of two and will ultimately be a value less than alignment which
+ // will be treated as invalid and result in OOM.
+ //
+ // Take for example a 64-bit system where the max power of two is (1UL << 63): 9223372036854775808
+ // * 9223372036854775808 + 9223372036854775807 == ulong.MaxValue, so no overflow
+ // * 9223372036854775809 + 9223372036854775807 == 0, so overflows and is less than alignment
+ // * ulong.MaxValue + 9223372036854775807 == 9223372036854775806, so overflows and is less than alignment
+ //
+ // Likewise, for small alignments such as 8 (which is the smallest on a 64-bit system for POSIX):
+ // * 18446744073709551608 + 7 == ulong.MaxValue, so no overflow
+ // * 18446744073709551609 + 7 == 0, so overflows and is less than alignment
+ // * ulong.MaxValue + 7 == 6, so overflows and is less than alignment
+
+ nuint maxAlignment = (nuint)1 << ((sizeof(nuint) * 8) - 1);
+ Assert.Throws(() => NativeMemory.AlignedAlloc(maxAlignment + 1, maxAlignment));
+
+ Assert.Throws(() => NativeMemory.AlignedAlloc(nuint.MaxValue, (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedAllocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(0, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedFreeTest()
+ {
+ // This should not throw
+ NativeMemory.AlignedFree(null);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ [InlineData(4)]
+ [InlineData(8)]
+ [InlineData(16)]
+ [InlineData(32)]
+ [InlineData(64)]
+ [InlineData(128)]
+ [InlineData(256)]
+ [InlineData(512)]
+ [InlineData(1 * 1024)]
+ [InlineData(2 * 1024)]
+ [InlineData(4 * 1024)]
+ [InlineData(8 * 1024)]
+ [InlineData(16 * 1024)]
+ [InlineData(64 * 1024)]
+ [InlineData(1 * 1024 * 1024)]
+ [InlineData(2 * 1024 * 1024)]
+ [InlineData(4 * 1024 * 1024)]
+ public void AlignedReallocTest(uint alignment)
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, alignment);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % alignment == 0);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, alignment);
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % alignment == 0);
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocLessThanVoidPtrAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, 1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, 1);
+ Assert.True(newPtr != null);
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrTest()
+ {
+ void* ptr = NativeMemory.AlignedRealloc(null, 1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.AlignedRealloc(null, nuint.MaxValue, (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedRealloc(null, 0, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocZeroAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), 0));
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocNonPowerOfTwoAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) + 1));
+ Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) * 3));
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 0, (uint)sizeof(nuint));
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocSmallerToLargerTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(16, 16);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % 16 == 0);
+
+ for (int i = 0; i < 16; i++)
+ {
+ ((byte*)ptr)[i] = (byte)i;
+ }
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 32, 16);
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % 16 == 0);
+
+ for (int i = 0; i < 16; i++)
+ {
+ Assert.True(((byte*)newPtr)[i] == i);
+ }
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AllocByteCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocElementCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(1, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocByteCountOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocElementCountOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.Alloc(1, nuint.MaxValue));
+ Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue, 1));
+ Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroByteCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroElementCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(0, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroElementSizeTest()
+ {
+ void* ptr = NativeMemory.Alloc(1, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedByteCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1);
+
+ Assert.True(ptr != null);
+ Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]);
+
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedElementCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1, 1);
+
+ Assert.True(ptr != null);
+ Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]);
+
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedByteCountOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroedElementCountOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.AllocZeroed(1, nuint.MaxValue));
+ Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue, 1));
+ Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroedZeroByteCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedZeroElementCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(0, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedZeroElementSizeTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void FreeTest()
+ {
+ // This should not throw
+ NativeMemory.Free(null);
+ }
+
+ [Fact]
+ public void ReallocTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.Realloc(ptr, 1);
+ Assert.True(newPtr != null);
+ NativeMemory.Free(newPtr);
+ }
+
+ [Fact]
+ public void ReallocNullPtrTest()
+ {
+ void* ptr = NativeMemory.Realloc(null, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void ReallocNullPtrOOMTest()
+ {
+ Assert.Throws(() => NativeMemory.Realloc(null, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void ReallocNullPtrZeroSizeTest()
+ {
+ void* ptr = NativeMemory.Realloc(null, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void ReallocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.Realloc(ptr, 0);
+ Assert.True(newPtr != null);
+ NativeMemory.Free(newPtr);
+ }
+
+ [Fact]
+ public void ReallocSmallerToLargerTest()
+ {
+ void* ptr = NativeMemory.Alloc(16);
+ Assert.True(ptr != null);
+
+ for (int i = 0; i < 16; i++)
+ {
+ ((byte*)ptr)[i] = (byte)i;
+ }
+
+ void* newPtr = NativeMemory.Realloc(ptr, 32);
+ Assert.True(newPtr != null);
+
+ for (int i = 0; i < 16; i++)
+ {
+ Assert.True(((byte*)newPtr)[i] == i);
+ }
+
+ NativeMemory.Free(newPtr);
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
index f05397e884c83..57ba4f3ec5298 100644
--- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
@@ -4,6 +4,7 @@
$(NetCoreAppCurrent)
true
deltascript.json
+ true
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
index e533377e673ac..2a953d19eece8 100644
--- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
@@ -18,6 +18,7 @@ namespace System.Reflection.Metadata
public class ApplyUpdateTest
{
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))]
void StaticMethodBodyUpdate()
{
ApplyUpdateUtil.TestCase(static () =>
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 39a1cfc4b816c..4f9afb90503f3 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -11651,6 +11651,12 @@ public enum LazyThreadSafetyMode
PublicationOnly = 1,
ExecutionAndPublication = 2,
}
+ public sealed class PeriodicTimer : System.IDisposable
+ {
+ public PeriodicTimer(System.TimeSpan period) { }
+ public System.Threading.Tasks.ValueTask WaitForNextTickAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; }
+ public void Dispose() { }
+ }
public static partial class Timeout
{
public const int Infinite = -1;
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
index 2a68cc12651e1..db3b3dcd1b5da 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
@@ -1,4 +1,4 @@
-
+
true
$(NoWarn),1718,SYSLIB0013
@@ -233,6 +233,7 @@
+
@@ -282,6 +283,7 @@
+
diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs
index cfe3690e588de..a80eb05598c7e 100644
--- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs
+++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Globalization;
+using System.IO;
using Xunit;
#pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0, NaN from -NaN
@@ -346,6 +347,70 @@ public static void Parse(string value, NumberStyles style, IFormatProvider provi
}
}
+ internal static string SplitPairs(string input)
+ {
+ string[] splitPairs = input.Split('-');
+ string ret = "";
+ foreach (var pair in splitPairs)
+ {
+ string reversedPair = Reverse(pair);
+ ret += reversedPair;
+ }
+
+ return ret;
+ }
+
+ internal static string Reverse(string s)
+ {
+ char[] charArray = s.ToCharArray();
+ Array.Reverse(charArray);
+ return new string(charArray);
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ public static void ParsePatterns()
+ {
+ string path = Directory.GetCurrentDirectory();
+ using (FileStream file = new FileStream(Path.Combine(path, "ibm-fpgen.txt"), FileMode.Open))
+ {
+ using (var streamReader = new StreamReader(file))
+ {
+ string line = streamReader.ReadLine();
+ while (line != null)
+ {
+ string[] data = line.Split(' ');
+ string inputHalfBytes = data[0];
+ string inputFloatBytes = data[1];
+ string inputDoubleBytes = data[2];
+ string correctValue = data[3];
+
+ double doubleValue = double.Parse(correctValue, NumberFormatInfo.InvariantInfo);
+ string doubleBytes = BitConverter.ToString(BitConverter.GetBytes(doubleValue));
+ float floatValue = float.Parse(correctValue, NumberFormatInfo.InvariantInfo);
+ string floatBytes = BitConverter.ToString(BitConverter.GetBytes(floatValue));
+ Half halfValue = Half.Parse(correctValue, NumberFormatInfo.InvariantInfo);
+ string halfBytes = BitConverter.ToString(BitConverter.GetBytes(halfValue));
+
+ doubleBytes = SplitPairs(doubleBytes);
+ floatBytes = SplitPairs(floatBytes);
+ halfBytes = SplitPairs(halfBytes);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ doubleBytes = Reverse(doubleBytes);
+ floatBytes = Reverse(floatBytes);
+ halfBytes = Reverse(halfBytes);
+ }
+
+ Assert.Equal(doubleBytes, inputDoubleBytes);
+ Assert.Equal(floatBytes, inputFloatBytes);
+ Assert.Equal(halfBytes, inputHalfBytes);
+ line = streamReader.ReadLine();
+ }
+ }
+ }
+ }
+
public static IEnumerable Parse_Invalid_TestData()
{
NumberStyles defaultStyle = NumberStyles.Float;
diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs
index a481ee253a55e..29fe532e6c0eb 100644
--- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs
+++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs
@@ -232,6 +232,7 @@ public void GetMethod()
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/51912", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/50831")]
public void GetMethods()
{
var methodNames = TestModule.GetMethods().Select(m => m.Name).ToArray();
diff --git a/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs
new file mode 100644
index 0000000000000..92367b8a46746
--- /dev/null
+++ b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs
@@ -0,0 +1,203 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Threading.Tests
+{
+ public class PeriodicTimerTests
+ {
+ [Fact]
+ public void Ctor_InvalidArguments_Throws()
+ {
+ AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(-1)));
+ AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.Zero));
+ AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue)));
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(uint.MaxValue - 1)]
+ public void Ctor_ValidArguments_Succeeds(uint milliseconds)
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(milliseconds));
+ }
+
+ [Fact]
+ public async Task Dispose_Idempotent()
+ {
+ var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1));
+
+ Assert.True(await timer.WaitForNextTickAsync());
+
+ for (int i = 0; i < 2; i++)
+ {
+ timer.Dispose();
+ Assert.False(timer.WaitForNextTickAsync().Result);
+
+ ((IDisposable)timer).Dispose();
+ Assert.False(timer.WaitForNextTickAsync().Result);
+ }
+ }
+
+ [Fact]
+ public async Task WaitForNextTickAsync_TimerFires_ReturnsTrue()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1));
+ await Task.Delay(100);
+ for (int i = 0; i < 3; i++)
+ {
+ Assert.True(await timer.WaitForNextTickAsync());
+ }
+ timer.Dispose();
+ Assert.False(timer.WaitForNextTickAsync().Result);
+ }
+
+ [Fact]
+ public async Task WaitForNextTickAsync_Dispose_ReturnsFalse()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1));
+ ValueTask task = timer.WaitForNextTickAsync();
+ timer.Dispose();
+ Assert.False(await task);
+ }
+
+ [Fact]
+ public async Task WaitForNextTickAsync_ConcurrentDispose_ReturnsFalse()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1));
+
+ _ = Task.Run(async delegate
+ {
+ await Task.Delay(1);
+ timer.Dispose();
+ });
+
+ Assert.False(await timer.WaitForNextTickAsync());
+ }
+
+ [Fact]
+ public async Task WaitForNextTickAsync_ConcurrentDisposeAfterTicks_EventuallyReturnsFalse()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1));
+
+ for (int i = 0; i < 5; i++)
+ {
+ Assert.True(await timer.WaitForNextTickAsync());
+ }
+
+ _ = Task.Run(async delegate
+ {
+ await Task.Delay(1);
+ timer.Dispose();
+ });
+
+ while (await timer.WaitForNextTickAsync());
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))]
+ public void PeriodicTimer_NoActiveOperations_TimerNotRooted()
+ {
+ WeakReference timer = Create();
+
+ WaitForTimerToBeCollected(timer, expected: true);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static WeakReference Create() =>
+ new WeakReference(new PeriodicTimer(TimeSpan.FromMilliseconds(1)));
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))]
+ public async Task PeriodicTimer_ActiveOperations_TimerRooted()
+ {
+ (WeakReference timer, ValueTask task) = Create();
+
+ WaitForTimerToBeCollected(timer, expected: false);
+
+ Assert.True(await task);
+
+ WaitForTimerToBeCollected(timer, expected: true);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static (WeakReference, ValueTask) Create()
+ {
+ var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1));
+ ValueTask task = timer.WaitForNextTickAsync();
+ return (new WeakReference(timer), task);
+ }
+ }
+
+ [Fact]
+ public void WaitForNextTickAsync_WaitAlreadyInProgress_Throws()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1));
+
+ ValueTask task = timer.WaitForNextTickAsync();
+ Assert.False(task.IsCompleted);
+
+ Assert.Throws(() => timer.WaitForNextTickAsync());
+
+ Assert.False(task.IsCompleted);
+
+ timer.Dispose();
+ Assert.True(task.IsCompleted);
+ Assert.False(task.Result);
+ }
+
+ [Fact]
+ public void WaitForNextTickAsync_CanceledBeforeWait_CompletesSynchronously()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1));
+
+ var cts = new CancellationTokenSource();
+ cts.Cancel();
+
+ ValueTask task = timer.WaitForNextTickAsync(cts.Token);
+ Assert.True(task.IsCanceled);
+ Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken);
+ }
+
+ [Fact]
+ public void WaitForNextTickAsync_CanceledAfterWait_CancelsOperation()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1));
+
+ var cts = new CancellationTokenSource();
+
+ ValueTask task = timer.WaitForNextTickAsync(cts.Token);
+ cts.Cancel();
+
+ Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken);
+ }
+
+ [Fact]
+ public async Task WaitForNextTickAsync_CanceledWaitThenWaitAgain_Succeeds()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1));
+
+ ValueTask task = timer.WaitForNextTickAsync(new CancellationToken(true));
+ Assert.ThrowsAny(() => task.Result);
+
+ var cts = new CancellationTokenSource();
+ task = timer.WaitForNextTickAsync(cts.Token);
+ cts.Cancel();
+ Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken);
+
+ for (int i = 0; i < 10; i++)
+ {
+ Assert.True(await timer.WaitForNextTickAsync());
+ }
+ }
+
+ private static void WaitForTimerToBeCollected(WeakReference timer, bool expected)
+ {
+ Assert.Equal(expected, SpinWait.SpinUntil(() =>
+ {
+ GC.Collect();
+ return !timer.TryGetTarget(out _);
+ }, TimeSpan.FromSeconds(expected ? 5 : 0.5)));
+ }
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props
index 7bdd796606be8..709a22a753708 100644
--- a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props
+++ b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props
@@ -2,7 +2,6 @@
Microsoft
- true
windows
\ No newline at end of file
diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs
index 9acecd8c695ca..1e1bf33769ff1 100644
--- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs
+++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs
@@ -74,7 +74,7 @@ public static void AddCertificateDisallowsNull(bool forReadOnly)
public static void AddCertificateDisallowedInReadOnly()
{
Pkcs12SafeContents contents = MakeReadonly(new Pkcs12SafeContents());
- X509Certificate2 cert = new X509Certificate2();
+ X509Certificate2 cert = new X509Certificate2(stackalloc byte[0]);
Assert.Throws(() => contents.AddCertificate(cert));
}
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs
index 0bf7a6e4b2236..e3ade1a368c51 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs
@@ -147,6 +147,7 @@ public override void CopyFrom(System.Security.Cryptography.AsnEncodedData asnEnc
}
public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable
{
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public X509Certificate() { }
public X509Certificate(byte[] data) { }
[System.CLSCompliantAttribute(false)]
@@ -200,13 +201,19 @@ protected virtual void Dispose(bool disposing) { }
public virtual string GetRawCertDataString() { throw null; }
public virtual byte[] GetSerialNumber() { throw null; }
public virtual string GetSerialNumberString() { throw null; }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(byte[] rawData) { }
[System.CLSCompliantAttribute(false)]
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(string fileName) { }
[System.CLSCompliantAttribute(false)]
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public virtual void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
public virtual void Reset() { }
void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object? sender) { }
@@ -217,6 +224,7 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser
}
public partial class X509Certificate2 : System.Security.Cryptography.X509Certificates.X509Certificate
{
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public X509Certificate2() { }
public X509Certificate2(byte[] rawData) { }
[System.CLSCompliantAttribute(false)]
@@ -265,13 +273,19 @@ public X509Certificate2(string fileName, string? password, System.Security.Crypt
public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPrivateKey() { throw null; }
public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPublicKey() { throw null; }
public string GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType nameType, bool forIssuer) { throw null; }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(byte[] rawData) { }
[System.CLSCompliantAttribute(false)]
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(string fileName) { }
[System.CLSCompliantAttribute(false)]
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
+ [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public override void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { }
public override void Reset() { }
public override string ToString() { throw null; }
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj
index d670fc85a01e4..66188cc890c48 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj
@@ -2,6 +2,7 @@
$(NetCoreAppCurrent)
enable
+ $(NoWarn);SYSLIB0026
@@ -15,4 +16,4 @@
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs
index cb949b8a93f7a..07959c3e77b5a 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs
@@ -399,7 +399,26 @@ private void EnsureCertData()
return;
Debug.Assert(!_certHandle.IsInvalid);
- _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle));
+ string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle);
+
+ try
+ {
+ _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle));
+ }
+ catch (CryptographicException e)
+ {
+ if (subjectSummary is null)
+ {
+ throw;
+ }
+
+ string message = SR.Format(
+ SR.Cryptography_X509_CertificateCorrupted,
+ subjectSummary);
+
+ throw new CryptographicException(message, e);
+ }
+
_readCertData = true;
}
}
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
index a5c33a43b40b8..322e2bab91d0c 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
@@ -331,6 +331,9 @@
The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate.
+
+ Certificate '{0}' is corrupted.
+
Enumeration has not started. Call MoveNext.
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
index 458fa92770709..2c0921f9fcd93 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
@@ -6,7 +6,7 @@
enable
- $(NoWarn);CS8769
+ $(NoWarn);CS8769;SYSLIB0026
SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported
@@ -22,6 +22,8 @@
Link="Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs" />
+
password,
}
[System.CLSCompliantAttribute(false)]
+#pragma warning disable SYSLIB0026
public X509Certificate(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) : this()
+#pragma warning restore SYSLIB0026
{
if (fileName == null)
throw new ArgumentNullException(nameof(fileName));
@@ -195,7 +199,9 @@ public X509Certificate(X509Certificate cert)
}
}
+#pragma warning disable SYSLIB0026
public X509Certificate(SerializationInfo info, StreamingContext context) : this()
+#pragma warning restore SYSLIB0026
{
throw new PlatformNotSupportedException();
}
@@ -569,33 +575,39 @@ public virtual string ToString(bool fVerbose)
return sb.ToString();
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(byte[] rawData)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
}
[System.CLSCompliantAttribute(false)]
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(string fileName)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(string fileName, string? password, X509KeyStorageFlags keyStorageFlags)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
}
[System.CLSCompliantAttribute(false)]
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual void Import(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags)
{
throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate);
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
index 067dbdd650f98..d4ef8220853cd 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
@@ -43,6 +43,7 @@ public override void Reset()
base.Reset();
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public X509Certificate2()
: base()
{
@@ -633,33 +634,39 @@ public override string ToString(bool verbose)
return sb.ToString();
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(byte[] rawData)
{
base.Import(rawData);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags)
{
base.Import(rawData, password, keyStorageFlags);
}
[System.CLSCompliantAttribute(false)]
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags)
{
base.Import(rawData, password, keyStorageFlags);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(string fileName)
{
base.Import(fileName);
}
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(string fileName, string? password, X509KeyStorageFlags keyStorageFlags)
{
base.Import(fileName, password, keyStorageFlags);
}
[System.CLSCompliantAttribute(false)]
+ [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public override void Import(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags)
{
base.Import(fileName, password, keyStorageFlags);
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
index a3ecd6dbeaaf8..ee68bbc75bcc3 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
@@ -4,6 +4,7 @@
$(DefineConstants);HAVE_THUMBPRINT_OVERLOADS
$(DefineConstants);Unix
true
+ $(NoWarn);SYSLIB0026
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS
diff --git a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs
index d2714f5a3b603..62703d868cc33 100644
--- a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs
+++ b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs
@@ -82,7 +82,7 @@ public static void HashMembershipConditionCallMethods()
[SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")]
public static void PublisherMembershipConditionCallMethods()
{
- PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate());
+ PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0]));
bool check = pmc.Check(new Evidence());
IMembershipCondition obj = pmc.Copy();
check = pmc.Equals(new object());
diff --git a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs
index af714344d0dbd..daa9d8ac55ae6 100644
--- a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs
+++ b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs
@@ -213,7 +213,7 @@ public static void PrincipalPermissionCallMethods()
[SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")]
public static void PublisherIdentityPermissionCallMethods()
{
- PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate());
+ PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0]));
PublisherIdentityPermission pip2 = new PublisherIdentityPermission(new Permissions.PermissionState());
IPermission ip = pip.Copy();
IPermission ip2 = pip.Intersect(ip);
diff --git a/src/libraries/System.Speech/src/System.Speech.csproj b/src/libraries/System.Speech/src/System.Speech.csproj
index a38b451935628..8493864d2f6d6 100644
--- a/src/libraries/System.Speech/src/System.Speech.csproj
+++ b/src/libraries/System.Speech/src/System.Speech.csproj
@@ -4,7 +4,7 @@
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0
- $(NoWarn);CS0649;SA1129
+ $(NoWarn);CS0649;SA1129;CA1847
false
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
index 4a2be44c8629c..89b9cdad1eeff 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
@@ -312,7 +312,9 @@ internal sealed override bool OnTryWrite(
if (!jsonPropertyInfo.GetMemberAndWriteJson(objectValue!, ref state, writer))
{
- Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value);
+ Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value ||
+ jsonPropertyInfo.ConverterBase.TypeToConvert == JsonTypeInfo.ObjectType);
+
return false;
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs
index 597dce6b7ef17..65b47d6507431 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs
@@ -24,7 +24,10 @@ public ObjectConverter()
public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
{
- throw new InvalidOperationException();
+ Debug.Assert(value?.GetType() == typeof(object));
+
+ writer.WriteStartObject();
+ writer.WriteEndObject();
}
internal override object ReadWithQuotes(ref Utf8JsonReader reader)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
index 6ea73dd49c9ec..33f45fd3ceda5 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
@@ -18,12 +18,11 @@ public abstract partial class JsonConverter : JsonConverter
///
protected internal JsonConverter()
{
- // Today only typeof(object) can have polymorphic writes.
- // In the future, this will be check for !IsSealed (and excluding value types).
- CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType;
- IsValueType = TypeToConvert.IsValueType;
- CanBeNull = !IsValueType || TypeToConvert.IsNullableOfT();
IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
+ // Today only the internal JsonConverter can have polymorphic writes.
+ CanBePolymorphic = IsInternalConverter && TypeToConvert == JsonTypeInfo.ObjectType;
+ IsValueType = TypeToConvert.IsValueType;
+ CanBeNull = default(T) is null;
if (HandleNull)
{
@@ -220,7 +219,12 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
// Remember if we were a continuation here since Push() may affect IsContinuation.
bool wasContinuation = state.IsContinuation;
+#if DEBUG
+ // DEBUG: ensure push/pop operations preserve stack integrity
+ JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo;
+#endif
state.Push();
+ Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type));
#if !DEBUG
// For performance, only perform validation on internal converters on debug builds.
@@ -257,6 +261,9 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
value = default;
state.Pop(true);
+#if DEBUG
+ Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo));
+#endif
return true;
}
@@ -288,6 +295,9 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
}
state.Pop(success);
+#if DEBUG
+ Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo));
+#endif
return success;
}
@@ -298,6 +308,9 @@ internal override sealed bool TryReadAsObject(ref Utf8JsonReader reader, JsonSer
return success;
}
+ ///
+ /// Overridden by the nullable converter to prevent boxing of values by the JIT.
+ ///
internal virtual bool IsNull(in T value) => value == null;
internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions options, ref WriteStack state)
@@ -307,7 +320,7 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth);
}
- if (CanBeNull && !HandleNullOnWrite && IsNull(value))
+ if (default(T) is null && !HandleNullOnWrite && IsNull(value))
{
// We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties were
// already handled in GetMemberAndWriteJson() so we don't need to check for IgnoreNullValues here.
@@ -317,81 +330,72 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
bool ignoreCyclesPopReference = false;
- if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
- // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string.
- ConverterStrategy != ConverterStrategy.Value &&
- !IsValueType && !IsNull(value))
+ if (
+#if NET5_0_OR_GREATER
+ !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
+#else
+ !IsValueType &&
+#endif
+ value is not null)
{
- // Custom (user) converters shall not track references
- // it is responsibility of the user to break cycles in case there's any
- // if we compare against Preserve, objects don't get preserved when a custom converter exists
- // given that the custom converter executes prior to the preserve logic.
- Debug.Assert(IsInternalConverter);
- Debug.Assert(value != null);
- ReferenceResolver resolver = state.ReferenceResolver;
-
- // Write null to break reference cycles.
- if (resolver.ContainsReferenceForCycleDetection(value))
+ if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
+ // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string.
+ ConverterStrategy != ConverterStrategy.Value)
{
- writer.WriteNullValue();
- return true;
- }
-
- // For boxed reference types: do not push when boxed in order to avoid false positives
- // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value.
- Debug.Assert(!CanBePolymorphic);
- resolver.PushReferenceForCycleDetection(value);
- ignoreCyclesPopReference = true;
- }
+ // Custom (user) converters shall not track references
+ // it is responsibility of the user to break cycles in case there's any
+ // if we compare against Preserve, objects don't get preserved when a custom converter exists
+ // given that the custom converter executes prior to the preserve logic.
+ Debug.Assert(IsInternalConverter);
- if (CanBePolymorphic)
- {
- if (value == null)
- {
- Debug.Assert(ConverterStrategy == ConverterStrategy.Value);
- Debug.Assert(!state.IsContinuation);
- Debug.Assert(HandleNullOnWrite);
+ ReferenceResolver resolver = state.ReferenceResolver;
- int originalPropertyDepth = writer.CurrentDepth;
- Write(writer, value, options);
- VerifyWrite(originalPropertyDepth, writer);
+ // Write null to break reference cycles.
+ if (resolver.ContainsReferenceForCycleDetection(value))
+ {
+ writer.WriteNullValue();
+ return true;
+ }
- return true;
+ // For boxed reference types: do not push when boxed in order to avoid false positives
+ // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value.
+ Debug.Assert(!CanBePolymorphic);
+ resolver.PushReferenceForCycleDetection(value);
+ ignoreCyclesPopReference = true;
}
- Type type = value.GetType();
- if (type == JsonTypeInfo.ObjectType)
+ if (CanBePolymorphic)
{
- writer.WriteStartObject();
- writer.WriteEndObject();
- return true;
- }
+ Debug.Assert(IsInternalConverter);
- if (type != TypeToConvert && IsInternalConverter)
- {
- // For internal converter only: Handle polymorphic case and get the new converter.
- // Custom converter, even though polymorphic converter, get called for reading AND writing.
- JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options);
- Debug.Assert(jsonConverter != this);
+ Type type = value.GetType();
- if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
- jsonConverter.IsValueType)
+ if (type != TypeToConvert)
{
- // For boxed value types: push the value before it gets unboxed on TryWriteAsObject.
- state.ReferenceResolver.PushReferenceForCycleDetection(value);
- ignoreCyclesPopReference = true;
- }
+ // For internal converter only: Handle polymorphic case and get the new converter.
+ // Custom converter, even though polymorphic converter, get called for reading AND writing.
+ JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options);
+ Debug.Assert(jsonConverter != this);
- // We found a different converter; forward to that.
- bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state);
+ if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
+ jsonConverter.IsValueType)
+ {
+ // For boxed value types: push the value before it gets unboxed on TryWriteAsObject.
+ state.ReferenceResolver.PushReferenceForCycleDetection(value);
+ ignoreCyclesPopReference = true;
+ }
- if (ignoreCyclesPopReference)
- {
- state.ReferenceResolver.PopReferenceForCycleDetection();
- }
+ // We found a different converter; forward to that.
+ bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state);
- return success2;
+ if (ignoreCyclesPopReference)
+ {
+ state.ReferenceResolver.PopReferenceForCycleDetection();
+ }
+
+ return success2;
+ }
}
}
@@ -411,12 +415,30 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
}
VerifyWrite(originalPropertyDepth, writer);
+
+ if (
+#if NET5_0_OR_GREATER
+ !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
+#endif
+ ignoreCyclesPopReference)
+ {
+ // should only be entered if we're serializing instances
+ // of type object using the internal object converter.
+ Debug.Assert(value?.GetType() == typeof(object) && IsInternalConverter);
+ state.ReferenceResolver.PopReferenceForCycleDetection();
+ }
+
return true;
}
bool isContinuation = state.IsContinuation;
+#if DEBUG
+ // DEBUG: ensure push/pop operations preserve stack integrity
+ JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo;
+#endif
state.Push();
+ Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type));
if (!isContinuation)
{
@@ -432,6 +454,9 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
}
state.Pop(success);
+#if DEBUG
+ Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo));
+#endif
if (ignoreCyclesPopReference)
{
@@ -476,6 +501,7 @@ internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, Json
// Ignore the naming policy for extension data.
state.Current.IgnoreDictionaryKeyPolicy = true;
+ state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
success = dictionaryConverter.OnWriteResume(writer, value, options, ref state);
if (success)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs
index 60836120d8907..cd6cd1b163b33 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs
@@ -43,6 +43,7 @@ private static void WriteUsingMetadata(Utf8JsonWriter writer, in TValue
typedInfo.Options._context?.CanUseSerializationLogic == true)
{
typedInfo.Serialize(writer, value);
+ writer.Flush();
}
else
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs
index 71c30c512756c..db52ffc079ffe 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs
@@ -125,9 +125,6 @@ private static string WriteUsingMetadata(in TValue value, JsonTypeInfo?
throw new ArgumentNullException(nameof(jsonTypeInfo));
}
- WriteStack state = default;
- state.Initialize(jsonTypeInfo, supportContinuation: false);
-
JsonSerializerOptions options = jsonTypeInfo.Options;
using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs
index 911ee9f3ede5f..8e268c2509940 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs
@@ -234,11 +234,17 @@ internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, U
{
T value = Get!(obj);
- if (Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
- value != null &&
+ if (
+#if NET5_0_OR_GREATER
+ !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
+#else
+ !Converter.IsValueType &&
+#endif
+ Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles &&
+ value is not null &&
// .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string.
// However JsonConverter that uses ConverterStrategy == Value is an exception.
- (Converter.CanBePolymorphic || (!Converter.IsValueType && ConverterStrategy != ConverterStrategy.Value)) &&
+ (Converter.CanBePolymorphic || ConverterStrategy != ConverterStrategy.Value) &&
state.ReferenceResolver.ContainsReferenceForCycleDetection(value))
{
// If a reference cycle is detected, treat value as null.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs
index 2f0347f68332a..c78646f45f4b1 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs
@@ -16,16 +16,24 @@ internal struct ReadStack
internal static readonly char[] SpecialCharacters = { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' };
///
- /// The number of stack frames when the continuation started.
+ /// Exposes the stackframe that is currently active.
///
- private int _continuationCount;
+ public ReadStackFrame Current;
+
+ ///
+ /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1.
+ ///
+ private ReadStackFrame[] _stack;
///
- /// The number of stack frames including Current. _previous will contain _count-1 higher frames.
+ /// Tracks the current depth of the stack.
///
private int _count;
- private List _previous;
+ ///
+ /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth.
+ ///
+ private int _continuationCount;
// State cache when deserializing objects with parameterized constructors.
private List? _ctorArgStateCache;
@@ -35,11 +43,10 @@ internal struct ReadStack
///
public long BytesConsumed;
- // A field is used instead of a property to avoid value semantics.
- public ReadStackFrame Current;
-
+ ///
+ /// Indicates that the state still contains suspended frames waiting re-entry.
+ ///
public bool IsContinuation => _continuationCount != 0;
- public bool IsLastContinuation => _continuationCount == _count;
///
/// Internal flag to let us know that we need to read ahead in the inner read loop.
@@ -59,25 +66,19 @@ internal struct ReadStack
///
public bool UseFastPath;
- private void AddCurrent()
+ ///
+ /// Ensures that the stack buffer has sufficient capacity to hold an additional frame.
+ ///
+ private void EnsurePushCapacity()
{
- if (_previous == null)
- {
- _previous = new List();
- }
-
- if (_count > _previous.Count)
+ if (_stack is null)
{
- // Need to allocate a new array element.
- _previous.Add(Current);
+ _stack = new ReadStackFrame[4];
}
- else
+ else if (_count - 1 == _stack.Length)
{
- // Use a previously allocated slot.
- _previous[_count - 1] = Current;
+ Array.Resize(ref _stack, 2 * _stack.Length);
}
-
- _count++;
}
public void Initialize(Type type, JsonSerializerOptions options, bool supportContinuation)
@@ -143,8 +144,10 @@ public void Push()
jsonTypeInfo = Current.JsonTypeInfo.ElementTypeInfo!;
}
- AddCurrent();
- Current.Reset();
+ EnsurePushCapacity();
+ _stack[_count - 1] = Current;
+ Current = default;
+ _count++;
Current.JsonTypeInfo = jsonTypeInfo;
Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo;
@@ -152,29 +155,26 @@ public void Push()
Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.NumberHandling;
}
}
- else if (_continuationCount == 1)
- {
- // No need for a push since there is only one stack frame.
- Debug.Assert(_count == 1);
- _continuationCount = 0;
- }
else
{
- // A continuation; adjust the index.
- Current = _previous[_count - 1];
-
- // Check if we are done.
- if (_count == _continuationCount)
+ // We are re-entering a continuation, adjust indices accordingly
+ if (_count++ > 0)
{
- _continuationCount = 0;
+ Current = _stack[_count - 1];
}
- else
+
+ // check if we are done
+ if (_continuationCount == _count)
{
- _count++;
+ _continuationCount = 0;
}
}
SetConstructorArgumentState();
+#if DEBUG
+ // Ensure the method is always exercised in debug builds.
+ _ = JsonPath();
+#endif
}
public void Pop(bool success)
@@ -188,41 +188,34 @@ public void Pop(bool success)
{
if (_count == 1)
{
- // No need for a continuation since there is only one stack frame.
+ // No need to copy any frames here.
_continuationCount = 1;
- }
- else
- {
- AddCurrent();
- _count--;
- _continuationCount = _count;
- _count--;
- Current = _previous[_count - 1];
+ _count = 0;
+ return;
}
- return;
+ // Need to push the Current frame to the stack,
+ // ensure that we have sufficient capacity.
+ EnsurePushCapacity();
+ _continuationCount = _count--;
}
-
- if (_continuationCount == 1)
+ else if (--_count == 0)
{
- // No need for a pop since there is only one stack frame.
- Debug.Assert(_count == 1);
+ // reached the root, no need to copy frames.
return;
}
- // Update the list entry to the current value.
- _previous[_count - 1] = Current;
-
- Debug.Assert(_count > 0);
+ _stack[_count] = Current;
+ Current = _stack[_count - 1];
}
else
{
Debug.Assert(_continuationCount == 0);
- }
- if (_count > 1)
- {
- Current = _previous[--_count -1];
+ if (--_count > 0)
+ {
+ Current = _stack[_count - 1];
+ }
}
SetConstructorArgumentState();
@@ -240,26 +233,25 @@ public string JsonPath()
for (int i = 0; i < count - 1; i++)
{
- AppendStackFrame(sb, _previous[i]);
+ AppendStackFrame(sb, ref _stack[i]);
}
if (_continuationCount == 0)
{
- AppendStackFrame(sb, Current);
+ AppendStackFrame(sb, ref Current);
}
return sb.ToString();
- static void AppendStackFrame(StringBuilder sb, in ReadStackFrame frame)
+ static void AppendStackFrame(StringBuilder sb, ref ReadStackFrame frame)
{
// Append the property name.
- string? propertyName = GetPropertyName(frame);
+ string? propertyName = GetPropertyName(ref frame);
AppendPropertyName(sb, propertyName);
if (frame.JsonTypeInfo != null && frame.IsProcessingEnumerable())
{
- IEnumerable? enumerable = (IEnumerable?)frame.ReturnValue;
- if (enumerable == null)
+ if (frame.ReturnValue is not IEnumerable enumerable)
{
return;
}
@@ -276,7 +268,7 @@ static void AppendStackFrame(StringBuilder sb, in ReadStackFrame frame)
}
}
- static int GetCount(IEnumerable enumerable)
+ static int GetCount(IEnumerable enumerable)
{
if (enumerable is ICollection collection)
{
@@ -311,7 +303,7 @@ static void AppendPropertyName(StringBuilder sb, string? propertyName)
}
}
- static string? GetPropertyName(in ReadStackFrame frame)
+ static string? GetPropertyName(ref ReadStackFrame frame)
{
string? propertyName = null;
@@ -350,10 +342,7 @@ private void SetConstructorArgumentState()
// A zero index indicates a new stack frame.
if (Current.CtorArgumentStateIndex == 0)
{
- if (_ctorArgStateCache == null)
- {
- _ctorArgStateCache = new List();
- }
+ _ctorArgStateCache ??= new List();
var newState = new ArgumentState();
_ctorArgStateCache.Add(newState);
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
index f4f64dcbb1a51..137305f1d5b43 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
@@ -89,20 +89,5 @@ public bool IsProcessingEnumerable()
{
return (JsonTypeInfo.PropertyInfoForTypeInfo.ConverterStrategy & ConverterStrategy.Enumerable) != 0;
}
-
- public void Reset()
- {
- CtorArgumentStateIndex = 0;
- CtorArgumentState = null;
- JsonTypeInfo = null!;
- ObjectState = StackFrameObjectState.None;
- OriginalDepth = 0;
- OriginalTokenType = JsonTokenType.None;
- PropertyIndex = 0;
- PropertyRefCache = null;
- ReturnValue = null;
-
- EndProperty();
- }
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs
index 22f67983ec346..e9a9f1e9e7546 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs
@@ -16,15 +16,25 @@ namespace System.Text.Json
internal struct WriteStack
{
///
- /// The number of stack frames when the continuation started.
+ /// Exposes the stackframe that is currently active.
///
- private int _continuationCount;
+ public WriteStackFrame Current;
+
+ ///
+ /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1.
+ ///
+ private WriteStackFrame[] _stack;
///
- /// The number of stack frames including Current. _previous will contain _count-1 higher frames.
+ /// Tracks the current depth of the stack.
///
private int _count;
+ ///
+ /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth.
+ ///
+ private int _continuationCount;
+
///
/// Cancellation token used by converters performing async serialization (e.g. IAsyncEnumerable)
///
@@ -41,17 +51,15 @@ internal struct WriteStack
///
public List? PendingAsyncDisposables;
- private List _previous;
-
- // A field is used instead of a property to avoid value semantics.
- public WriteStackFrame Current;
-
///
/// The amount of bytes to write before the underlying Stream should be flushed and the
/// current buffer adjusted to remove the processed bytes.
///
public int FlushThreshold;
+ ///
+ /// Indicates that the state still contains suspended frames waiting re-entry.
+ ///
public bool IsContinuation => _continuationCount != 0;
// The bag of preservable references.
@@ -62,25 +70,16 @@ internal struct WriteStack
///
public bool SupportContinuation;
- private void AddCurrent()
+ private void EnsurePushCapacity()
{
- if (_previous == null)
+ if (_stack is null)
{
- _previous = new List();
+ _stack = new WriteStackFrame[4];
}
-
- if (_count > _previous.Count)
+ else if (_count - 1 == _stack.Length)
{
- // Need to allocate a new array element.
- _previous.Add(Current);
+ Array.Resize(ref _stack, 2 * _stack.Length);
}
- else
- {
- // Use a previously allocated slot.
- _previous[_count - 1] = Current;
- }
-
- _count++;
}
///
@@ -125,8 +124,10 @@ public void Push()
JsonTypeInfo jsonTypeInfo = Current.GetPolymorphicJsonPropertyInfo().RuntimeTypeInfo;
JsonNumberHandling? numberHandling = Current.NumberHandling;
- AddCurrent();
- Current.Reset();
+ EnsurePushCapacity();
+ _stack[_count - 1] = Current;
+ Current = default;
+ _count++;
Current.JsonTypeInfo = jsonTypeInfo;
Current.DeclaredJsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo;
@@ -134,27 +135,25 @@ public void Push()
Current.NumberHandling = numberHandling ?? Current.DeclaredJsonPropertyInfo.NumberHandling;
}
}
- else if (_continuationCount == 1)
- {
- // No need for a push since there is only one stack frame.
- Debug.Assert(_count == 1);
- _continuationCount = 0;
- }
else
{
- // A continuation, adjust the index.
- Current = _previous[_count - 1];
-
- // Check if we are done.
- if (_count == _continuationCount)
+ // We are re-entering a continuation, adjust indices accordingly
+ if (_count++ > 0)
{
- _continuationCount = 0;
+ Current = _stack[_count - 1];
}
- else
+
+ // check if we are done
+ if (_continuationCount == _count)
{
- _count++;
+ _continuationCount = 0;
}
}
+
+#if DEBUG
+ // Ensure the method is always exercised in debug builds.
+ _ = PropertyPath();
+#endif
}
public void Pop(bool success)
@@ -168,33 +167,25 @@ public void Pop(bool success)
{
if (_count == 1)
{
- // No need for a continuation since there is only one stack frame.
+ // No need to copy any frames here.
_continuationCount = 1;
- _count = 1;
- }
- else
- {
- AddCurrent();
- _count--;
- _continuationCount = _count;
- _count--;
- Current = _previous[_count - 1];
+ _count = 0;
+ return;
}
- return;
+ // Need to push the Current frame to the stack,
+ // ensure that we have sufficient capacity.
+ EnsurePushCapacity();
+ _continuationCount = _count--;
}
-
- if (_continuationCount == 1)
+ else if (--_count == 0)
{
- // No need for a pop since there is only one stack frame.
- Debug.Assert(_count == 1);
+ // reached the root, no need to copy frames.
return;
}
- // Update the list entry to the current value.
- _previous[_count - 1] = Current;
-
- Debug.Assert(_count > 0);
+ _stack[_count] = Current;
+ Current = _stack[_count - 1];
}
else
{
@@ -207,11 +198,11 @@ public void Pop(bool success)
PendingAsyncDisposables ??= new List();
PendingAsyncDisposables.Add(Current.AsyncEnumerator);
}
- }
- if (_count > 1)
- {
- Current = _previous[--_count - 1];
+ if (--_count > 0)
+ {
+ Current = _stack[_count - 1];
+ }
}
}
@@ -253,13 +244,10 @@ public void DisposePendingDisposablesOnException()
DisposeFrame(Current.CollectionEnumerator, ref exception);
int stackSize = Math.Max(_count, _continuationCount);
- if (stackSize > 1)
+ for (int i = 0; i < stackSize - 1; i++)
{
- for (int i = 0; i < stackSize - 1; i++)
- {
- Debug.Assert(_previous[i].AsyncEnumerator is null);
- DisposeFrame(_previous[i].CollectionEnumerator, ref exception);
- }
+ Debug.Assert(_stack[i].AsyncEnumerator is null);
+ DisposeFrame(_stack[i].CollectionEnumerator, ref exception);
}
if (exception is not null)
@@ -294,12 +282,9 @@ public async ValueTask DisposePendingDisposablesOnExceptionAsync()
exception = await DisposeFrame(Current.CollectionEnumerator, Current.AsyncEnumerator, exception).ConfigureAwait(false);
int stackSize = Math.Max(_count, _continuationCount);
- if (stackSize > 1)
+ for (int i = 0; i < stackSize - 1; i++)
{
- for (int i = 0; i < stackSize - 1; i++)
- {
- exception = await DisposeFrame(_previous[i].CollectionEnumerator, _previous[i].AsyncEnumerator, exception).ConfigureAwait(false);
- }
+ exception = await DisposeFrame(_stack[i].CollectionEnumerator, _stack[i].AsyncEnumerator, exception).ConfigureAwait(false);
}
if (exception is not null)
@@ -343,17 +328,17 @@ public string PropertyPath()
for (int i = 0; i < count - 1; i++)
{
- AppendStackFrame(sb, _previous[i]);
+ AppendStackFrame(sb, ref _stack[i]);
}
if (_continuationCount == 0)
{
- AppendStackFrame(sb, Current);
+ AppendStackFrame(sb, ref Current);
}
return sb.ToString();
- void AppendStackFrame(StringBuilder sb, in WriteStackFrame frame)
+ static void AppendStackFrame(StringBuilder sb, ref WriteStackFrame frame)
{
// Append the property name.
string? propertyName = frame.DeclaredJsonPropertyInfo?.MemberInfo?.Name;
@@ -366,7 +351,7 @@ void AppendStackFrame(StringBuilder sb, in WriteStackFrame frame)
AppendPropertyName(sb, propertyName);
}
- void AppendPropertyName(StringBuilder sb, string? propertyName)
+ static void AppendPropertyName(StringBuilder sb, string? propertyName)
{
if (propertyName != null)
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs
index b162b55709c30..7d9ed3b6863f3 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs
@@ -120,20 +120,5 @@ public JsonConverter InitializeReEntry(Type type, JsonSerializerOptions options)
return PolymorphicJsonPropertyInfo.ConverterBase;
}
-
- public void Reset()
- {
- CollectionEnumerator = null;
- EnumeratorIndex = 0;
- AsyncEnumerator = null;
- AsyncEnumeratorIsPendingCompletion = false;
- IgnoreDictionaryKeyPolicy = false;
- JsonTypeInfo = null!;
- OriginalDepth = 0;
- ProcessedStartToken = false;
- ProcessedEndToken = false;
-
- EndProperty();
- }
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs
index 53474be5f1e3f..daa3a4cb71ec9 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using Xunit;
@@ -111,5 +112,16 @@ public static IEnumerable GetIncompatibleOptions()
yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { DefaultIgnoreCondition = JsonIgnoreCondition.Never } };
yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { IgnoreReadOnlyFields = true } };
}
+
+ [Fact]
+ public static void WriterIsFlushedAtRootCall()
+ {
+ using MemoryStream ms = new();
+ using Utf8JsonWriter writer = new(ms);
+
+ JsonSerializer.Serialize(writer, new HighLowTemps(), SerializationContext.Default.HighLowTemps);
+ Assert.Equal(18, writer.BytesCommitted);
+ Assert.Equal(0, writer.BytesPending);
+ }
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs
index 0ab7ad020de5d..36309616eaf5f 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs
@@ -307,7 +307,9 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
- throw new InvalidOperationException("Should not get here.");
+ Assert.IsType(value);
+ writer.WriteStartObject();
+ writer.WriteEndObject();
}
}
@@ -732,5 +734,23 @@ static void Verify(JsonSerializerOptions options)
options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter());
Verify(options);
}
+
+ [Fact]
+ public static void CanCustomizeSystemObjectSerialization()
+ {
+ var options = new JsonSerializerOptions { Converters = { new CustomSystemObjectConverter() } };
+
+ string expectedJson = "42";
+ string actualJson = JsonSerializer.Serialize(new object(), options);
+ Assert.Equal(expectedJson, actualJson);
+ }
+
+ private class CustomSystemObjectConverter : JsonConverter
+ {
+ public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
+ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
+ => writer.WriteNumberValue(42);
+ }
}
+
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs
index 06747c8778626..f7d80c2a01e63 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs
@@ -328,12 +328,12 @@ public static void JsonEncodedTextStringsCustomAllowAll(string message, string e
[Fact]
public static void Options_GetConverterForObjectJsonElement_GivesCorrectConverter()
{
- GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "[3]", true);
+ GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "{}");
JsonElement element = JsonDocument.Parse("[3]").RootElement;
- GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]", false);
+ GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]");
}
- private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue, bool throws)
+ private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue)
{
var options = new JsonSerializerOptions();
@@ -347,10 +347,7 @@ private static void GenericObjectOrJsonElementConverterTestHelper(string conv
if (readValue is JsonElement element)
{
- Assert.Equal(JsonValueKind.Array, element.ValueKind);
- JsonElement.ArrayEnumerator iterator = element.EnumerateArray();
- Assert.True(iterator.MoveNext());
- Assert.Equal(3, iterator.Current.GetInt32());
+ JsonTestHelper.AssertJsonEqual(stringValue, element.ToString());
}
else
{
@@ -360,22 +357,14 @@ private static void GenericObjectOrJsonElementConverterTestHelper(string conv
using (var stream = new MemoryStream())
using (var writer = new Utf8JsonWriter(stream))
{
- if (throws)
- {
- Assert.Throws(() => converter.Write(writer, (T)objectValue, options));
- Assert.Throws(() => converter.Write(writer, (T)objectValue, null));
- }
- else
- {
- converter.Write(writer, (T)objectValue, options);
- writer.Flush();
- Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
-
- writer.Reset(stream);
- converter.Write(writer, (T)objectValue, null); // Test with null option
- writer.Flush();
- Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
- }
+ converter.Write(writer, (T)objectValue, options);
+ writer.Flush();
+ Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
+
+ writer.Reset(stream);
+ converter.Write(writer, (T)objectValue, null); // Test with null option
+ writer.Flush();
+ Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs
index 8157f9bbdf3c9..235fa8010deb8 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs
@@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Tests
public class ReferenceHandlerTests_IgnoreCycles
{
private static readonly JsonSerializerOptions s_optionsIgnoreCycles =
- new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };
+ new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles, DefaultBufferSize = 1 };
[Fact]
public async Task IgnoreCycles_OnObject()
@@ -401,6 +401,25 @@ public async void IgnoreCycles_BoxedValueShouldNotBeIgnored()
await Test_Serialize_And_SerializeAsync_Contains(root, expectedSubstring: @"""DayOfBirth"":15", expectedTimes: 2, s_optionsIgnoreCycles);
}
+ [Fact]
+ public async Task CycleDetectionStatePersistsAcrossContinuations()
+ {
+ string expectedValueJson = @"{""LargePropertyName"":""A large-ish string to force continuations"",""Nested"":null}";
+ var recVal = new RecursiveValue { LargePropertyName = "A large-ish string to force continuations" };
+ recVal.Nested = recVal;
+
+ var value = new List { recVal, recVal };
+ string expectedJson = $"[{expectedValueJson},{expectedValueJson}]";
+
+ await Test_Serialize_And_SerializeAsync(value, expectedJson, s_optionsIgnoreCycles);
+ }
+
+ public class RecursiveValue
+ {
+ public string LargePropertyName { get; set; }
+ public RecursiveValue? Nested { get; set; }
+ }
+
private async Task Test_Serialize_And_SerializeAsync(T obj, string expected, JsonSerializerOptions options)
{
string json;
diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj
index 2f4d0949db3ef..383dd088e127c 100644
--- a/src/libraries/libraries-packages.proj
+++ b/src/libraries/libraries-packages.proj
@@ -1,20 +1,11 @@
-
- $(AdditionalBuildTargetFrameworks);package-$(Configuration)
-
-
-
- $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\
- $(PackagingTaskAssembly)netcoreapp3.1\
- $(PackagingTaskAssembly)net472\
- $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll
-
-
+
+
@@ -31,7 +22,6 @@
ones that might do this. After we ship a stable set of packages this target should be ran and the
changes to the package index should be commited to the repo.
-->
-
diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj
index 574bee9c7dce5..12d402fcb7e39 100644
--- a/src/libraries/sendtohelixhelp.proj
+++ b/src/libraries/sendtohelixhelp.proj
@@ -29,6 +29,7 @@
<_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">00:45:00
<_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00
<_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:00:00
+ <_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00
<_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00
<_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00
<_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == ''">00:30:00
@@ -277,6 +278,7 @@
$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk'))
$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'RemoteLoopServer', '$(NetCoreAppCurrent)-$(Configuration)'))
@@ -311,6 +313,7 @@
+
diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj
index b1ca6c3a1ddba..b5638f2a00daa 100644
--- a/src/libraries/tests.proj
+++ b/src/libraries/tests.proj
@@ -255,6 +255,8 @@
+
+
@@ -276,6 +278,9 @@
+
+
+
@@ -317,10 +322,8 @@
-
-
diff --git a/src/mono/mono.proj b/src/mono/mono.proj
index e5d4bb2970aa3..e459f87217716 100644
--- a/src/mono/mono.proj
+++ b/src/mono/mono.proj
@@ -294,7 +294,6 @@
<_MonoCMakeArgs Include="-DENABLE_INTERP_LIB=1"/>
<_MonoCMakeArgs Include="-DDISABLE_ICALL_TABLES=1"/>
<_MonoCMakeArgs Include="-DDISABLE_CRASH_REPORTING=1"/>
- <_MonoCMakeArgs Include="-DDISABLE_COMPONENTS=1"/>
<_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/>
<_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/>
<_MonoCMakeArgs Include="-DENABLE_LLVM_RUNTIME=1"/>
diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c
index 517c45b8d67dd..ec5b180b3a1f8 100644
--- a/src/mono/mono/component/debugger-agent.c
+++ b/src/mono/mono/component/debugger-agent.c
@@ -111,8 +111,7 @@
#define DISABLE_SOCKET_TRANSPORT
#endif
-#ifndef DISABLE_SDB
-
+#if !defined (DISABLE_SDB) || defined(TARGET_WASM)
#include
#include
@@ -147,28 +146,6 @@ typedef struct {
gboolean using_icordbg;
} AgentConfig;
-typedef struct _InvokeData InvokeData;
-
-struct _InvokeData
-{
- int id;
- int flags;
- guint8 *p;
- guint8 *endp;
- /* This is the context which needs to be restored after the invoke */
- MonoContext ctx;
- gboolean has_ctx;
- /*
- * If this is set, invoke this method with the arguments given by ARGS.
- */
- MonoMethod *method;
- gpointer *args;
- guint32 suspend_count;
- int nmethods;
-
- InvokeData *last_invoke;
-};
-
struct _DebuggerTlsData {
MonoThreadUnwindState context;
@@ -264,7 +241,6 @@ struct _DebuggerTlsData {
gboolean gc_finalizing;
};
-
/* Buffered reply packets */
static ReplyPacket reply_packets [128];
static int nreply_packets;
@@ -308,7 +284,9 @@ typedef struct {
/*
* Globals
*/
-
+#ifdef TARGET_WASM
+static DebuggerTlsData debugger_wasm_thread;
+#endif
static AgentConfig agent_config;
/*
@@ -391,6 +369,28 @@ static gint32 suspend_count;
/* Whenever to buffer reply messages and send them together */
static gboolean buffer_replies;
+
+#ifndef TARGET_WASM
+#define GET_TLS_DATA_FROM_THREAD(thread) \
+ DebuggerTlsData *tls = NULL; \
+ mono_loader_lock(); \
+ if (thread_to_tls != NULL) \
+ tls = (DebuggerTlsData*)mono_g_hash_table_lookup(thread_to_tls, thread); \
+ mono_loader_unlock();
+#define GET_DEBUGGER_TLS() \
+ DebuggerTlsData *tls; \
+ tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id);
+#else
+#define GET_TLS_DATA_FROM_THREAD(thread) \
+ DebuggerTlsData *tls; \
+ tls = &debugger_wasm_thread;
+#define GET_DEBUGGER_TLS() \
+ DebuggerTlsData *tls; \
+ tls = &debugger_wasm_thread;
+#endif
+
+//mono_native_tls_get_value (debugger_tls_id);
+
#define dbg_lock mono_de_lock
#define dbg_unlock mono_de_unlock
@@ -454,6 +454,7 @@ static void objrefs_init (void);
static void objrefs_cleanup (void);
static void ids_init (void);
+
static void ids_cleanup (void);
static void suspend_init (void);
@@ -472,19 +473,25 @@ static MonoContext* tls_get_restore_state (void *the_tls);
static gboolean try_process_suspend (void *tls, MonoContext *ctx, gboolean from_breakpoint);
static gboolean begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal);
static void begin_single_step_processing (MonoContext *ctx, gboolean from_signal);
-static void ss_discard_frame_context (void *the_tls);
-static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes);
static gboolean ensure_jit (DbgEngineStackFrame* the_frame);
static int ensure_runtime_is_suspended (void);
-static int get_this_async_id (DbgEngineStackFrame *frame);
-static void* create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind);
-static void process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset);
-static int ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args);
-static void ss_args_destroy (SingleStepArgs *ss_args);
static int handle_multiple_ss_requests (void);
+/* Callbacks used by wasm debugger */
+static MdbgProtErrorCode mono_process_dbg_packet (int id, MdbgProtCommandSet command_set, int command, gboolean *no_reply, guint8 *p, guint8 *end, MdbgProtBuffer *buf);
+static void mono_init_debugger_agent_for_wasm (int log_level);
+static void* mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, MdbgProtEventKind kind);
+static void mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset);
+static void mono_dbg_debugger_agent_user_break (void);
+static void mono_wasm_save_thread_context (void);
+static DebuggerTlsData* mono_wasm_get_tls (void);
+static MdbgProtErrorCode mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp);
+static void mono_ss_discard_frame_context (void *the_tls);
+static void mono_ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes);
+
static GENERATE_TRY_GET_CLASS_WITH_CACHE (fixed_buffer, "System.Runtime.CompilerServices", "FixedBufferAttribute")
+
#ifndef DISABLE_SOCKET_TRANSPORT
static void
register_socket_transport (void);
@@ -554,7 +561,7 @@ parse_flag (const char *option, char *flag)
}
}
-void
+static void
debugger_agent_parse_options (char *options)
{
char **args, **ptr;
@@ -704,17 +711,17 @@ debugger_agent_init (void)
cbs.try_process_suspend = try_process_suspend;
cbs.begin_breakpoint_processing = begin_breakpoint_processing;
cbs.begin_single_step_processing = begin_single_step_processing;
- cbs.ss_discard_frame_context = ss_discard_frame_context;
- cbs.ss_calculate_framecount = ss_calculate_framecount;
+ cbs.ss_discard_frame_context = mono_ss_discard_frame_context;
+ cbs.ss_calculate_framecount = mono_ss_calculate_framecount;
cbs.ensure_jit = ensure_jit;
cbs.ensure_runtime_is_suspended = ensure_runtime_is_suspended;
- cbs.get_this_async_id = get_this_async_id;
+ cbs.get_this_async_id = mono_get_this_async_id;
cbs.set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag;
cbs.get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method;
- cbs.create_breakpoint_events = create_breakpoint_events;
- cbs.process_breakpoint_events = process_breakpoint_events;
- cbs.ss_create_init_args = ss_create_init_args;
- cbs.ss_args_destroy = ss_args_destroy;
+ cbs.create_breakpoint_events = mono_dbg_create_breakpoint_events;
+ cbs.process_breakpoint_events = mono_dbg_process_breakpoint_events;
+ cbs.ss_create_init_args = mono_ss_create_init_args;
+ cbs.ss_args_destroy = mono_ss_args_destroy;
cbs.handle_multiple_ss_requests = handle_multiple_ss_requests;
mono_component_debugger ()->mono_de_init (&cbs);
@@ -1575,6 +1582,25 @@ static GHashTable *obj_to_objref;
/* Protected by the dbg lock */
static MonoGHashTable *suspended_objs;
+#ifdef TARGET_WASM
+static void
+mono_init_debugger_agent_for_wasm (int log_level_parm)
+{
+ if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1)
+ return;
+
+ ids_init();
+ objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref);
+ obj_to_objref = g_hash_table_new (NULL, NULL);
+
+ log_level = log_level;
+ event_requests = g_ptr_array_new ();
+ vm_start_event_sent = TRUE;
+ transport = &transports [0];
+ memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData));
+ agent_config.enabled = TRUE;
+}
+#endif
static void
@@ -1971,7 +1997,6 @@ static int
buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val)
{
int id = get_id (domain, type, val);
-
buffer_add_id (buf, id);
return id;
}
@@ -2161,6 +2186,21 @@ save_thread_context (MonoContext *ctx)
mono_thread_state_init_from_current (&tls->context);
}
+#ifdef TARGET_WASM
+static void
+mono_wasm_save_thread_context (void)
+{
+ debugger_wasm_thread.really_suspended = TRUE;
+ mono_thread_state_init_from_current (&debugger_wasm_thread.context);
+}
+
+static DebuggerTlsData*
+mono_wasm_get_tls (void)
+{
+ return &debugger_wasm_thread;
+}
+#endif
+
static MonoCoopMutex suspend_mutex;
/* Cond variable used to wait for suspend_count becoming 0 */
@@ -2685,7 +2725,8 @@ static int
count_threads_to_wait_for (void)
{
int count = 0;
-
+ if (thread_to_tls == NULL)
+ return 0;
mono_loader_lock ();
mono_g_hash_table_foreach (thread_to_tls, count_thread, &count);
mono_loader_unlock ();
@@ -3034,7 +3075,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f
tls->frames = new_frames;
tls->frame_count = new_frame_count;
tls->frames_up_to_date = TRUE;
-
+#ifndef TARGET_WASM
if (CHECK_PROTOCOL_VERSION (2, 52)) {
MonoJitTlsData *jit_data = thread->thread_info->jit_data;
gboolean has_interp_resume_state = FALSE;
@@ -3049,6 +3090,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f
}
}
}
+#endif
}
/*
@@ -3479,7 +3521,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
return;
}
}
-
+
if (event == EVENT_KIND_VM_START)
suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE;
@@ -3537,12 +3579,10 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
break;
case EVENT_KIND_BREAKPOINT:
case EVENT_KIND_STEP: {
- DebuggerTlsData *tls;
- tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id);
+ GET_DEBUGGER_TLS();
g_assert (tls);
mono_stopwatch_stop (&tls->step_time);
MonoMethod *method = (MonoMethod *)arg;
-
buffer_add_methodid (&buf, domain, method);
buffer_add_long (&buf, il_offset);
break;
@@ -3563,6 +3603,9 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
case EVENT_KIND_EXCEPTION: {
EventInfo *ei = (EventInfo *)arg;
buffer_add_objid (&buf, ei->exc);
+#ifdef TARGET_WASM
+ buffer_add_byte (&buf, ei->caught);
+#endif
/*
* We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it
* later after the suspension. (#12494).
@@ -3571,8 +3614,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
break;
}
case EVENT_KIND_USER_BREAK: {
- DebuggerTlsData *tls;
- tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id);
+ GET_DEBUGGER_TLS();
g_assert (tls);
// We are already processing a breakpoint event
if (tls->disable_breakpoints)
@@ -4028,13 +4070,17 @@ event_requests_cleanup (void)
* Ensure DebuggerTlsData fields are filled out.
*/
static void
-ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes)
+mono_ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes)
{
DebuggerTlsData *tls = (DebuggerTlsData*)the_tls;
-
+#ifndef TARGET_WASM
if (force_use_ctx || !tls->context.valid)
mono_thread_state_init_from_monoctx (&tls->context, ctx);
compute_frame_info (tls->thread, tls, FALSE);
+#else
+ compute_frame_info (tls->thread, tls, TRUE);
+#endif
+
if (frames)
*frames = (DbgEngineStackFrame**)tls->frames;
if (nframes)
@@ -4047,7 +4093,7 @@ ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx
* Discard frame data and invalidate any context
*/
static void
-ss_discard_frame_context (void *the_tls)
+mono_ss_discard_frame_context (void *the_tls)
{
DebuggerTlsData *tls = (DebuggerTlsData*)the_tls;
tls->context.valid = FALSE;
@@ -4092,8 +4138,8 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not
//since thread probably changed...
-static int
-get_this_async_id (DbgEngineStackFrame *frame)
+int
+mono_get_this_async_id (DbgEngineStackFrame *frame)
{
MonoClassField *builder_field;
gpointer builder;
@@ -4146,8 +4192,11 @@ begin_breakpoint_processing (void *the_tls, MonoContext *ctx, MonoJitInfo *ji, g
* Skip the instruction causing the breakpoint signal.
*/
if (from_signal)
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
mono_arch_skip_breakpoint (ctx, ji);
-
+#else
+ NOT_IMPLEMENTED;
+#endif
if (tls->disable_breakpoints)
return FALSE;
return TRUE;
@@ -4160,7 +4209,7 @@ typedef struct {
} BreakPointEvents;
static void*
-create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
+mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
{
int suspend_policy = 0;
BreakPointEvents *evts = g_new0 (BreakPointEvents, 1);
@@ -4177,7 +4226,7 @@ create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *j
}
static void
-process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset)
+mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset)
{
BreakPointEvents *evts = (BreakPointEvents*)_evts;
/*
@@ -4287,7 +4336,7 @@ user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data)
* Called by System.Diagnostics.Debugger:Break ().
*/
static void
-debugger_agent_user_break (void)
+mono_dbg_debugger_agent_user_break (void)
{
if (agent_config.enabled) {
MonoContext ctx;
@@ -4317,7 +4366,11 @@ static void
begin_single_step_processing (MonoContext *ctx, gboolean from_signal)
{
if (from_signal)
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
mono_arch_skip_single_step (ctx);
+#else
+ NOT_IMPLEMENTED;
+#endif
}
static void
@@ -4349,7 +4402,11 @@ debugger_agent_single_step_event (void *sigctx)
MonoContext ctx;
mono_sigctx_to_monoctx (sigctx, &ctx);
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
mono_arch_skip_single_step (&ctx);
+#else
+ NOT_IMPLEMENTED;
+#endif
mono_monoctx_to_sigctx (&ctx, sigctx);
return;
}
@@ -4429,8 +4486,8 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx)
if (MONO_CONTEXT_GET_IP (ctx) == orig_ip - 1)
MONO_CONTEXT_SET_IP (ctx, orig_ip);
}
-static void
-ss_args_destroy (SingleStepArgs *ss_args)
+void
+mono_ss_args_destroy (SingleStepArgs *ss_args)
{
if (ss_args->frames)
free_frames ((StackFrame**)ss_args->frames, ss_args->nframes);
@@ -4455,8 +4512,8 @@ ensure_runtime_is_suspended (void)
return ERR_NONE;
}
-static int
-ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args)
+int
+mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args)
{
MonoSeqPointInfo *info = NULL;
gboolean found_sp;
@@ -4466,10 +4523,9 @@ ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args)
gboolean set_ip = FALSE;
StackFrame **frames = NULL;
int nframes = 0;
-
- mono_loader_lock ();
- DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, ss_req->thread);
- mono_loader_unlock ();
+
+ GET_TLS_DATA_FROM_THREAD (ss_req->thread);
+
g_assert (tls);
if (!tls->context.valid) {
PRINT_DEBUG_MSG (1, "Received a single step request on a thread with no managed frames.\n");
@@ -4721,23 +4777,15 @@ debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
while (1)
;
}
-
int i, j, suspend_policy;
GSList *events;
MonoJitInfo *ji, *catch_ji;
EventInfo ei;
- DebuggerTlsData *tls = NULL;
-
- if (thread_to_tls != NULL) {
- MonoInternalThread *thread = mono_thread_internal_current ();
-
- mono_loader_lock ();
- tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread);
- mono_loader_unlock ();
-
- if (tls && tls->abort_requested)
+ GET_TLS_DATA_FROM_THREAD (mono_thread_internal_current ());
+ if (tls != NULL) {
+ if (tls->abort_requested)
return;
- if (tls && tls->disable_breakpoints)
+ if (tls->disable_breakpoints)
return;
}
@@ -4950,7 +4998,10 @@ buffer_add_info_for_null_value (Buffer* buf, MonoType* t, MonoDomain* domain)
buffer_add_int (buf, m_class_get_rank (mono_class_from_mono_type_internal (t)));
if (m_class_get_byval_arg (m_class_get_element_class (mono_class_from_mono_type_internal (t)))->type == MONO_TYPE_CLASS)
buffer_add_typeid (buf, domain, m_class_get_element_class (mono_class_from_mono_type_internal (t)));
+ buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t));
break;
+ default:
+ buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t));
}
}
/*
@@ -5134,6 +5185,9 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
buffer_add_byte (buf, MONO_TYPE_VALUETYPE);
buffer_add_byte (buf, m_class_is_enumtype (klass));
+
+ if (CHECK_PROTOCOL_VERSION(2, 61))
+ buffer_add_byte(buf, boxed_vtype);
buffer_add_typeid (buf, domain, klass);
nfields = 0;
@@ -5207,9 +5261,8 @@ decode_vtype (MonoType *t, MonoDomain *domain, gpointer void_addr, gpointer void
ErrorCode err;
is_enum = decode_byte (buf, &buf, limit);
- /* Enums are sent as a normal vtype */
- if (is_enum)
- return ERR_NOT_IMPLEMENTED;
+ if (CHECK_PROTOCOL_VERSION(2, 61))
+ decode_byte (buf, &buf, limit);
klass = decode_typeid (buf, &buf, limit, &d, &err);
if (err != ERR_NONE)
return err;
@@ -5398,7 +5451,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
handle_ref:
default:
if (MONO_TYPE_IS_REFERENCE (t)) {
- if (type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) {
+ if (type == MONO_TYPE_CLASS || type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) {
int objid = decode_objid (buf, &buf, limit);
ErrorCode err;
MonoObject *obj;
@@ -5420,7 +5473,12 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
mono_gc_wbarrier_generic_store_internal (addr, obj);
} else if (type == VALUE_TYPE_ID_NULL) {
+ if (CHECK_PROTOCOL_VERSION (2, 59)) {
+ decode_byte (buf, &buf, limit);
+ decode_int (buf, &buf, limit); //not used
+ }
*(MonoObject**)addr = NULL;
+
} else if (type == MONO_TYPE_VALUETYPE) {
ERROR_DECL (error);
guint8 *buf2;
@@ -5437,8 +5495,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
*/
buf2 = buf;
is_enum = decode_byte (buf, &buf, limit);
- if (is_enum)
- return ERR_NOT_IMPLEMENTED;
+ decode_byte (buf, &buf, limit); //ignore is boxed
klass = decode_typeid (buf, &buf, limit, &d, &err);
if (err != ERR_NONE)
return err;
@@ -5899,7 +5956,7 @@ add_thread (gpointer key, gpointer value, gpointer user_data)
static ErrorCode
-do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp)
+mono_do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp)
{
ERROR_DECL (error);
guint8 *end = invoke->endp;
@@ -5966,7 +6023,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8
return err;
}
} else {
- if (!(m->flags & METHOD_ATTRIBUTE_STATIC && CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later
+ if (!(m->flags & METHOD_ATTRIBUTE_STATIC) || (m->flags & METHOD_ATTRIBUTE_STATIC && !CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later
err = decode_value(m_class_get_byval_arg(m->klass), domain, this_buf, p, &p, end, FALSE);
if (err != ERR_NONE)
return err;
@@ -6221,7 +6278,7 @@ invoke_method (void)
if (err) {
/* Fail the other invokes as well */
} else {
- err = do_invoke_method (tls, &buf, invoke, p, &p);
+ err = mono_do_invoke_method (tls, &buf, invoke, p, &p);
}
if (tls->abort_requested) {
@@ -6914,6 +6971,39 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
buffer_add_byte_array (buf, memory, size);
break;
}
+ case MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME: {
+ int i;
+ char* assembly_name = decode_string (p, &p, end);
+ //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
+ char *lookup_name = g_strdup (assembly_name);
+ for (i = strlen (lookup_name) - 1; i >= 0; --i) {
+ if (lookup_name [i] == '.') {
+ lookup_name [i] = 0;
+ break;
+ }
+ }
+
+ //resolve the assembly
+ MonoImageOpenStatus status;
+ MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
+ if (!aname) {
+ PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name);
+ buffer_add_int(buf, -1);
+ break;
+ }
+ MonoAssemblyByNameRequest byname_req;
+ mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ());
+ MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status);
+ g_free (lookup_name);
+ mono_assembly_name_free_internal (aname);
+ if (!assembly) {
+ PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name);
+ buffer_add_int(buf, -1);
+ break;
+ }
+ buffer_add_assemblyid (buf, mono_get_root_domain (), assembly);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
@@ -7076,10 +7166,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
g_free (req);
return err;
}
+
+ GET_TLS_DATA_FROM_THREAD (THREAD_TO_INTERNAL(step_thread));
- mono_loader_lock ();
- DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL(step_thread));
- mono_loader_unlock ();
g_assert (tls);
if (tls->terminated) {
@@ -7093,6 +7182,22 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
g_free (req);
return err;
}
+#ifdef TARGET_WASM
+ int isBPOnManagedCode = 0;
+ SingleStepReq *ss_req = req->info;
+ if (ss_req && ss_req->bps) {
+ GSList *l;
+
+ for (l = ss_req->bps; l; l = l->next) {
+ if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
+ isBPOnManagedCode = 1;
+ }
+ }
+ if (!isBPOnManagedCode) {
+ mono_de_cancel_all_ss ();
+ }
+ buffer_add_byte (buf, isBPOnManagedCode);
+#endif
} else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) {
req->info = mono_de_set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req, NULL);
} else if (req->event_kind == EVENT_KIND_METHOD_EXIT) {
@@ -7723,7 +7828,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
buffer_add_string (buf, m_class_get_name_space (klass));
buffer_add_string (buf, m_class_get_name (klass));
// FIXME: byref
- name = mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME);
+
+ MonoTypeNameFormat format = MONO_TYPE_NAME_FORMAT_FULL_NAME;
+ if (CHECK_PROTOCOL_VERSION(2, 61))
+ format = (MonoTypeNameFormat) decode_int (p, &p, end);
+ name = mono_type_get_name_full (m_class_get_byval_arg (klass), format);
buffer_add_string (buf, name);
g_free (name);
buffer_add_assemblyid (buf, domain, m_class_get_image (klass)->assembly);
@@ -8167,6 +8276,23 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
buffer_add_int (buf, value_size);
break;
}
+ case MDBGPROT_CMD_TYPE_GET_PARENTS: {
+ MonoClass *parent_klass = m_class_get_parent (klass);
+ int count = 0;
+ while (parent_klass != NULL)
+ {
+ count++;
+ parent_klass = m_class_get_parent (parent_klass);
+ }
+ buffer_add_int (buf, count);
+ parent_klass = m_class_get_parent (klass);
+ while (parent_klass != NULL)
+ {
+ buffer_add_typeid (buf, domain, parent_klass);
+ parent_klass = m_class_get_parent (parent_klass);
+ }
+ break;
+ }
default:
err = ERR_NOT_IMPLEMENTED;
goto exit;
@@ -8222,7 +8348,11 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
switch (command) {
case CMD_METHOD_GET_NAME: {
buffer_add_string (buf, method->name);
- break;
+ break;
+ }
+ case MDBGPROT_CMD_METHOD_GET_NAME_FULL: {
+ buffer_add_string (buf, mono_method_full_name (method, FALSE));
+ break;
}
case MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN: {
buffer_add_int (buf, m_class_get_type_token (method->klass));
@@ -8661,6 +8791,16 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
buffer_add_assemblyid(buf, mono_domain_get (), m_class_get_image(method->klass)->assembly);
break;
}
+ case MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO: {
+ MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method);
+ if (async_method) {
+ buffer_add_byte(buf, TRUE);
+ mono_debug_free_method_async_debug_info (async_method);
+ }
+ else
+ buffer_add_byte(buf, FALSE);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
@@ -8746,7 +8886,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
break;
}
case MDBGPROT_CMD_THREAD_GET_CONTEXT: {
- DebuggerTlsData *tls;
int start_frame;
while (!is_suspended ()) {
if (suspend_count)
@@ -8754,9 +8893,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
}
start_frame = decode_int (p, &p, end);
- mono_loader_lock ();
- tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread);
- mono_loader_unlock ();
+ GET_TLS_DATA_FROM_THREAD (thread);
if (tls == NULL)
return ERR_UNLOADED;
@@ -8769,7 +8906,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
break;
}
case CMD_THREAD_GET_FRAME_INFO: {
- DebuggerTlsData *tls;
int i, start_frame, length;
// Wait for suspending if it already started
@@ -8790,10 +8926,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
if (start_frame != 0 || length != -1)
return ERR_NOT_IMPLEMENTED;
-
- mono_loader_lock ();
- tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread);
- mono_loader_unlock ();
+ GET_TLS_DATA_FROM_THREAD (thread);
if (tls == NULL)
return ERR_UNLOADED;
@@ -8965,7 +9098,6 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
MonoThread *thread_obj;
MonoInternalThread *thread;
int pos, i, len, frame_idx;
- DebuggerTlsData *tls;
StackFrame *frame;
MonoDebugMethodJitInfo *jit;
MonoMethodSignature *sig;
@@ -8982,9 +9114,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
id = decode_id (p, &p, end);
- mono_loader_lock ();
- tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread);
- mono_loader_unlock ();
+ GET_TLS_DATA_FROM_THREAD (thread);
g_assert (tls);
for (i = 0; i < tls->frame_count; ++i) {
@@ -8995,7 +9125,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
return ERR_INVALID_FRAMEID;
/* The thread is still running native code, can't get frame variables info */
- if (!tls->really_suspended && !tls->async_state.valid)
+ if (!tls->really_suspended && !tls->async_state.valid)
return ERR_NOT_SUSPENDED;
frame_idx = i;
frame = tls->frames [frame_idx];
@@ -9559,6 +9689,17 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type_internal (((MonoReflectionType*)obj->vtable->type)->type));
buffer_add_domainid (buf, obj->vtable->domain);
break;
+ case MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD:
+ buffer_add_methodid (buf, obj->vtable->domain, ((MonoDelegate *)obj)->method);
+ break;
+ case MDBGPROT_CMD_OBJECT_IS_DELEGATE: {
+ MonoType *type = m_class_get_byval_arg (obj_type);
+ if (m_class_is_delegate (obj_type) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class)))
+ buffer_add_byte (buf, TRUE);
+ else
+ buffer_add_byte (buf, FALSE);
+ break;
+ }
default:
err = ERR_NOT_IMPLEMENTED;
goto exit;
@@ -9875,6 +10016,62 @@ wait_for_attach (void)
return TRUE;
}
+static ErrorCode
+mono_process_dbg_packet (int id, CommandSet command_set, int command, gboolean *no_reply, guint8 *buf, guint8 *end, Buffer *ret_buf)
+{
+ ErrorCode err;
+ /* Process the request */
+ switch (command_set) {
+ case CMD_SET_VM:
+ err = vm_commands (command, id, buf, end, ret_buf);
+ if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD)
+ /* Sent after the invoke is complete */
+ *no_reply = TRUE;
+ break;
+ case CMD_SET_EVENT_REQUEST:
+ err = event_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_APPDOMAIN:
+ err = domain_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_ASSEMBLY:
+ err = assembly_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_MODULE:
+ err = module_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_FIELD:
+ err = field_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_TYPE:
+ err = type_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_METHOD:
+ err = method_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_THREAD:
+ err = thread_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_STACK_FRAME:
+ err = frame_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_ARRAY_REF:
+ err = array_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_STRING_REF:
+ err = string_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_POINTER:
+ err = pointer_commands (command, buf, end, ret_buf);
+ break;
+ case CMD_SET_OBJECT_REF:
+ err = object_commands (command, buf, end, ret_buf);
+ break;
+ default:
+ err = ERR_NOT_IMPLEMENTED;
+ }
+ return err;
+}
/*
* debugger_thread:
*
@@ -9970,57 +10167,7 @@ debugger_thread (void *arg)
err = ERR_NONE;
no_reply = FALSE;
-
- /* Process the request */
- switch (command_set) {
- case CMD_SET_VM:
- err = vm_commands (command, id, p, end, &buf);
- if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD)
- /* Sent after the invoke is complete */
- no_reply = TRUE;
- break;
- case CMD_SET_EVENT_REQUEST:
- err = event_commands (command, p, end, &buf);
- break;
- case CMD_SET_APPDOMAIN:
- err = domain_commands (command, p, end, &buf);
- break;
- case CMD_SET_ASSEMBLY:
- err = assembly_commands (command, p, end, &buf);
- break;
- case CMD_SET_MODULE:
- err = module_commands (command, p, end, &buf);
- break;
- case CMD_SET_FIELD:
- err = field_commands (command, p, end, &buf);
- break;
- case CMD_SET_TYPE:
- err = type_commands (command, p, end, &buf);
- break;
- case CMD_SET_METHOD:
- err = method_commands (command, p, end, &buf);
- break;
- case CMD_SET_THREAD:
- err = thread_commands (command, p, end, &buf);
- break;
- case CMD_SET_STACK_FRAME:
- err = frame_commands (command, p, end, &buf);
- break;
- case CMD_SET_ARRAY_REF:
- err = array_commands (command, p, end, &buf);
- break;
- case CMD_SET_STRING_REF:
- err = string_commands (command, p, end, &buf);
- break;
- case CMD_SET_POINTER:
- err = pointer_commands (command, p, end, &buf);
- break;
- case CMD_SET_OBJECT_REF:
- err = object_commands (command, p, end, &buf);
- break;
- default:
- err = ERR_NOT_IMPLEMENTED;
- }
+ err = mono_process_dbg_packet (id, command_set, command, &no_reply, p, end, &buf);
if (command_set == CMD_SET_VM && command == CMD_VM_START_BUFFERING) {
buffer_replies = TRUE;
@@ -10096,14 +10243,25 @@ debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table)
fn_table->handle_exception = debugger_agent_handle_exception;
fn_table->begin_exception_filter = debugger_agent_begin_exception_filter;
fn_table->end_exception_filter = debugger_agent_end_exception_filter;
- fn_table->user_break = debugger_agent_user_break;
+ fn_table->user_break = mono_dbg_debugger_agent_user_break;
fn_table->debug_log = debugger_agent_debug_log;
fn_table->debug_log_is_enabled = debugger_agent_debug_log_is_enabled;
fn_table->send_crash = mono_debugger_agent_send_crash;
fn_table->register_transport = register_transport;
fn_table->mono_debugger_agent_parse_options = mono_debugger_agent_parse_options;
+
+ //used externally by wasm debugger
fn_table->mono_debugger_agent_transport_handshake = mono_debugger_agent_transport_handshake;
+ fn_table->mono_process_dbg_packet = mono_process_dbg_packet;
+ fn_table->mono_init_debugger_agent_for_wasm = mono_init_debugger_agent_for_wasm;
+ fn_table->mono_dbg_create_breakpoint_events = mono_dbg_create_breakpoint_events;
+ fn_table->mono_dbg_process_breakpoint_events = mono_dbg_process_breakpoint_events;
+ fn_table->mono_wasm_save_thread_context = mono_wasm_save_thread_context;
+ fn_table->mono_wasm_get_tls = mono_wasm_get_tls;
+ fn_table->mono_do_invoke_method = mono_do_invoke_method;
+ fn_table->mono_ss_discard_frame_context = mono_ss_discard_frame_context;
+ fn_table->mono_ss_calculate_framecount = mono_ss_calculate_framecount;
}
diff --git a/src/mono/mono/component/debugger-engine.c b/src/mono/mono/component/debugger-engine.c
index fc5eca66c7433..1a1cd78d3bc97 100644
--- a/src/mono/mono/component/debugger-engine.c
+++ b/src/mono/mono/component/debugger-engine.c
@@ -391,13 +391,6 @@ collect_domain_bp (gpointer key, gpointer value, gpointer user_data)
jit_mm_unlock (jit_mm);
}
-void
-mono_de_clear_all_breakpoints (void)
-{
- while (breakpoints->len)
- mono_de_clear_breakpoint ((MonoBreakpoint*)g_ptr_array_index (breakpoints, 0));
-}
-
/*
* mono_de_set_breakpoint:
*
diff --git a/src/mono/mono/component/debugger-engine.h b/src/mono/mono/component/debugger-engine.h
index 275f0381670b7..f042fc38a912e 100644
--- a/src/mono/mono/component/debugger-engine.h
+++ b/src/mono/mono/component/debugger-engine.h
@@ -408,3 +408,12 @@ void win32_debugger_log(FILE *stream, const gchar *format, ...);
#define PRINT_ERROR_MSG(...) g_printerr (__VA_ARGS__)
#define PRINT_MSG(...) g_print (__VA_ARGS__)
#endif
+
+int
+mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args);
+
+void
+mono_ss_args_destroy (SingleStepArgs *ss_args);
+
+int
+mono_get_this_async_id (DbgEngineStackFrame *frame);
diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c
index 358c4e60c53fd..b36fea0cdf422 100644
--- a/src/mono/mono/mini/aot-compiler.c
+++ b/src/mono/mono/mini/aot-compiler.c
@@ -1115,17 +1115,15 @@ arch_init (MonoAotCompile *acfg)
acfg->llvm_label_prefix = "";
acfg->user_symbol_prefix = "";
-#if defined(TARGET_X86)
-#ifdef TARGET_ANDROID
- g_string_append_printf (acfg->llc_args, " -mtriple=i686-none-linux-android21");
-#else
+#if TARGET_X86 || TARGET_AMD64
const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu;
- g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic");
#endif
+
+#if defined(TARGET_X86)
+ g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic");
#endif
#if defined(TARGET_AMD64)
- const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu;
g_string_append_printf (acfg->llc_args, " -march=x86-64 %s", has_custom_args ? "" : "-mcpu=generic");
/* NOP */
acfg->align_pad_value = 0x90;
@@ -1159,7 +1157,13 @@ arch_init (MonoAotCompile *acfg)
g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16 -float-abi=hard");
g_string_append (acfg->as_args, " -mfpu=vfp3");
#elif defined(ARM_FPU_VFP)
+
+#if defined(TARGET_ARM)
+ // +d16 triggers a warning on arm
+ g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon");
+#else
g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16");
+#endif
g_string_append (acfg->as_args, " -mfpu=vfp3");
#else
g_string_append (acfg->llc_args, " -mattr=+soft-float");
diff --git a/src/mono/mono/mini/debugger-protocol.c b/src/mono/mono/mini/debugger-protocol.c
index 1402dae3d36ef..2fe3096608a51 100644
--- a/src/mono/mono/mini/debugger-protocol.c
+++ b/src/mono/mono/mini/debugger-protocol.c
@@ -56,7 +56,6 @@ m_dbgprot_decode_int (uint8_t *buf, uint8_t **endbuf, uint8_t *limit)
{
*endbuf = buf + 4;
g_assert (*endbuf <= limit);
-
return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0);
}
diff --git a/src/mono/mono/mini/debugger-protocol.h b/src/mono/mono/mini/debugger-protocol.h
index 87d9e232c851a..24e8a2e42c27e 100644
--- a/src/mono/mono/mini/debugger-protocol.h
+++ b/src/mono/mono/mini/debugger-protocol.h
@@ -34,7 +34,8 @@ typedef enum {
MDBGPROT_CMD_VM_START_BUFFERING = 14,
MDBGPROT_CMD_VM_STOP_BUFFERING = 15,
MDBGPROT_CMD_VM_READ_MEMORY = 16,
- MDBGPROT_CMD_VM_WRITE_MEMORY = 17
+ MDBGPROT_CMD_VM_WRITE_MEMORY = 17,
+ MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME = 18
} MdbgProtCmdVM;
typedef enum {
@@ -173,7 +174,9 @@ typedef enum {
MDBGPROT_CMD_METHOD_MAKE_GENERIC_METHOD = 10,
MDBGPROT_CMD_METHOD_TOKEN = 11,
MDBGPROT_CMD_METHOD_ASSEMBLY = 12,
- MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13
+ MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13,
+ MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO = 14,
+ MDBGPROT_CMD_METHOD_GET_NAME_FULL = 15
} MdbgProtCmdMethod;
typedef enum {
@@ -197,7 +200,8 @@ typedef enum {
MDBGPROT_CMD_TYPE_IS_INITIALIZED = 18,
MDBGPROT_CMD_TYPE_CREATE_INSTANCE = 19,
MDBGPROT_CMD_TYPE_GET_VALUE_SIZE = 20,
- MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21
+ MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21,
+ MDBGPROT_CMD_TYPE_GET_PARENTS = 22
} MdbgProtCmdType;
typedef enum {
@@ -235,7 +239,9 @@ typedef enum {
MDBGPROT_CMD_OBJECT_REF_GET_DOMAIN = 5,
MDBGPROT_CMD_OBJECT_REF_SET_VALUES = 6,
MDBGPROT_CMD_OBJECT_REF_GET_INFO = 7,
- MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8
+ MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8,
+ MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD = 9,
+ MDBGPROT_CMD_OBJECT_IS_DELEGATE = 10
} MdbgProtCmdObject;
typedef enum {
diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c
index 1f4ec81d761f4..4fc43ee307166 100644
--- a/src/mono/mono/mini/mini-arm.c
+++ b/src/mono/mono/mini/mini-arm.c
@@ -873,6 +873,8 @@ mono_arch_init (void)
have a way to properly detect CPU features on it. */
thumb_supported = TRUE;
iphone_abi = TRUE;
+#elif defined(TARGET_ANDROID)
+ thumb_supported = TRUE;
#else
thumb_supported = mono_hwcap_arm_has_thumb;
thumb2_supported = mono_hwcap_arm_has_thumb2;
diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c
index 132b9c309887d..346b4bd43f2ef 100644
--- a/src/mono/mono/mini/mini-posix.c
+++ b/src/mono/mono/mini/mini-posix.c
@@ -136,18 +136,31 @@ mono_runtime_shutdown_handlers (void)
static GHashTable *mono_saved_signal_handlers = NULL;
static struct sigaction *
-get_saved_signal_handler (int signo, gboolean remove)
+get_saved_signal_handler (int signo)
{
if (mono_saved_signal_handlers) {
/* The hash is only modified during startup, so no need for locking */
struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo));
- if (remove && handler)
- g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo));
return handler;
}
return NULL;
}
+
+static void
+remove_saved_signal_handler (int signo)
+{
+ if (mono_saved_signal_handlers) {
+ /* The hash is only modified during startup, so no need for locking */
+ struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo));
+ if (handler)
+ g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo));
+ }
+ return;
+}
+
+
+
static void
save_old_signal_handler (int signo, struct sigaction *old_action)
{
@@ -182,7 +195,7 @@ gboolean
MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal)
{
int signal = MONO_SIG_HANDLER_GET_SIGNO ();
- struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal, FALSE);
+ struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal);
if (saved_handler && saved_handler->sa_handler) {
if (!(saved_handler->sa_flags & SA_SIGINFO)) {
@@ -377,7 +390,7 @@ static void
remove_signal_handler (int signo)
{
struct sigaction sa;
- struct sigaction *saved_action = get_saved_signal_handler (signo, TRUE);
+ struct sigaction *saved_action = get_saved_signal_handler (signo);
if (!saved_action) {
sa.sa_handler = SIG_DFL;
@@ -388,6 +401,7 @@ remove_signal_handler (int signo)
} else {
g_assert (sigaction (signo, saved_action, NULL) != -1);
}
+ remove_saved_signal_handler(signo);
}
void
diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/mini/mini-wasm-debugger.c
index 8bf756f1e42d1..999b4aa1694f5 100644
--- a/src/mono/mono/mini/mini-wasm-debugger.c
+++ b/src/mono/mono/mini/mini-wasm-debugger.c
@@ -10,9 +10,10 @@
#include
#include
#include
-#include
-#include
#include
+#include
+#include
+#include
//XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle
#include
@@ -26,84 +27,29 @@
static int log_level = 1;
-enum {
- EXCEPTION_MODE_NONE,
- EXCEPTION_MODE_UNCAUGHT,
- EXCEPTION_MODE_ALL
-};
-
-// Flags for get_*_properties
-#define GPFLAG_NONE 0x0000
-#define GPFLAG_OWN_PROPERTIES 0x0001
-#define GPFLAG_ACCESSORS_ONLY 0x0002
-#define GPFLAG_EXPAND_VALUETYPES 0x0004
-
//functions exported to be used by JS
G_BEGIN_DECLS
-EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset);
-EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id);
-EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void);
-EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_local_vars (int scope, int* pos, int len);
-EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void);
-EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind);
-EMSCRIPTEN_KEEPALIVE int mono_wasm_pause_on_exceptions (int state);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, int gpflags);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_object (int object_id, const char* name);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass);
EMSCRIPTEN_KEEPALIVE void mono_wasm_set_is_debugger_attached (gboolean is_attached);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value);
-EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_value_on_object (int object_id, const char* name, const char* value);
+EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size);
+EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue);
+
//JS functions imported that we use
-extern void mono_wasm_add_frame (int il_offset, int method_token, int frame_id, const char *assembly_name, const char *method_name);
-extern void mono_wasm_fire_bp (void);
-extern void mono_wasm_fire_exception (int exception_obj_id, const char* message, const char* class_name, gboolean uncaught);
-extern void mono_wasm_add_obj_var (const char*, const char*, guint64);
-extern void mono_wasm_add_enum_var (const char*, const char*, guint64);
-extern void mono_wasm_add_func_var (const char*, const char*, guint64);
-extern void mono_wasm_add_properties_var (const char*, gint32);
-extern void mono_wasm_add_array_item (int);
-extern void mono_wasm_set_is_async_method (guint64);
-extern void mono_wasm_add_typed_value (const char *type, const char *str_value, double value);
+extern void mono_wasm_fire_debugger_agent_message (void);
extern void mono_wasm_asm_loaded (const char *asm_name, const char *assembly_data, guint32 assembly_len, const char *pdb_data, guint32 pdb_len);
G_END_DECLS
-static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags);
static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame);
+static gboolean receive_debugger_agent_message (void *data, int len);
static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly);
-static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error);
//FIXME move all of those fields to the profiler object
static gboolean debugger_enabled;
static gboolean has_pending_lazy_loaded_assemblies;
-static int event_request_id;
-static GHashTable *objrefs;
-static GHashTable *obj_to_objref;
-static int objref_id = 0;
-static int pause_on_exc = EXCEPTION_MODE_NONE;
-static MonoObject* exception_on_runtime_invoke = NULL;
-
-static const char*
-all_getters_allowed_class_names[] = {
- "System.DateTime",
- "System.DateTimeOffset",
- "System.TimeSpan"
-};
-
-static const char*
-to_string_as_descr_names[] = {
- "System.DateTime",
- "System.DateTimeOffset",
- "System.Decimal",
- "System.TimeSpan"
-};
#define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
@@ -131,14 +77,6 @@ void wasm_debugger_log (int level, const gchar *format, ...)
g_free (mesg);
}
-static void
-inplace_tolower (char *c)
-{
- int i;
- for (i = strlen (c) - 1; i >= 0; --i)
- c [i] = tolower (c [i]);
-}
-
static void
jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
{
@@ -151,78 +89,6 @@ appdomain_load (MonoProfiler *prof, MonoDomain *domain)
mono_component_debugger ()->mono_de_domain_add (domain);
}
-/* Frame state handling */
-static GPtrArray *frames;
-
-static void
-free_frame (DbgEngineStackFrame *frame)
-{
- g_free (frame);
-}
-
-static gboolean
-collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
-{
- SeqPoint sp;
- MonoMethod *method;
-
- //skip wrappers
- if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
- return FALSE;
-
- if (info->ji)
- method = jinfo_get_method (info->ji);
- else
- method = info->method;
-
- if (!method)
- return FALSE;
-
- PRINT_DEBUG_MSG (2, "collect_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type);
-
- if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp))
- PRINT_DEBUG_MSG (2, "collect_frames: Failed to lookup sequence point. method: %s, native_offset: %d \n", method->name, info->native_offset);
-
-
- StackFrame *frame = g_new0 (StackFrame, 1);
- frame->de.ji = info->ji;
- frame->de.domain = mono_get_root_domain ();
- frame->de.method = method;
- frame->de.native_offset = info->native_offset;
-
- frame->il_offset = info->il_offset;
- frame->interp_frame = info->interp_frame;
- frame->frame_addr = info->frame_addr;
-
- g_ptr_array_add (frames, frame);
-
- return FALSE;
-}
-
-static void
-free_frame_state (void)
-{
- if (frames) {
- int i;
- for (i = 0; i < frames->len; ++i)
- free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
- g_ptr_array_set_size (frames, 0);
- }
-}
-
-static void
-compute_frames (void) {
- if (frames) {
- int i;
- for (i = 0; i < frames->len; ++i)
- free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
- g_ptr_array_set_size (frames, 0);
- } else {
- frames = g_ptr_array_new ();
- }
-
- mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL);
-}
static MonoContext*
tls_get_restore_state (void *tls)
{
@@ -249,17 +115,14 @@ begin_single_step_processing (MonoContext *ctx, gboolean from_signal)
static void
ss_discard_frame_context (void *the_tls)
{
- free_frame_state ();
+ mono_component_debugger ()->mono_ss_discard_frame_context (mono_component_debugger ()->mono_wasm_get_tls ());
}
static void
ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes)
{
- compute_frames ();
- if (out_frames)
- *out_frames = (DbgEngineStackFrame **)frames->pdata;
- if (nframes)
- *nframes = frames->len;
+ mono_component_debugger ()->mono_wasm_save_thread_context ();
+ mono_component_debugger ()->mono_ss_calculate_framecount (mono_component_debugger ()->mono_wasm_get_tls (), NULL, force_use_ctx, out_frames, nframes);
}
static gboolean
@@ -274,135 +137,11 @@ ensure_runtime_is_suspended (void)
return DE_ERR_NONE;
}
-static int
-get_object_id (MonoObject *obj)
-{
- ObjRef *ref;
- if (!obj)
- return 0;
-
- ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
- if (ref)
- return ref->id;
- ref = g_new0 (ObjRef, 1);
- ref->id = mono_atomic_inc_i32 (&objref_id);
- ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE);
- g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
- g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
- return ref->id;
-}
-
-
-static int
-get_this_async_id (DbgEngineStackFrame *frame)
-{
- MonoClassField *builder_field;
- gpointer builder;
- MonoMethod *method;
- MonoObject *ex;
- ERROR_DECL (error);
- MonoObject *obj;
-
- /*
- * FRAME points to a method in a state machine class/struct.
- * Call the ObjectIdForDebugger method of the associated method builder type.
- */
- builder = mono_component_debugger ()->get_async_method_builder (frame);
- if (!builder)
- return 0;
-
- builder_field = mono_class_get_field_from_name_full (mono_component_debugger ()->get_class_to_get_builder_field(frame), "<>t__builder", NULL);
- if (!builder_field)
- return 0;
-
- method = mono_component_debugger ()->get_object_id_for_debugger_method (mono_class_from_mono_type_internal (builder_field->type));
- if (!method) {
- return 0;
- }
-
- obj = mono_runtime_try_invoke_internal (method, builder, NULL, &ex, error);
- mono_error_assert_ok (error);
-
- return get_object_id (obj);
-}
-
-typedef struct {
- gboolean is_ss; //do I need this?
-} BpEvents;
-
-static void*
-create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
-{
- PRINT_DEBUG_MSG (1, "ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len);
- if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) {
- BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events
- evts->is_ss = (ss_reqs && ss_reqs->len);
- return evts;
- }
- return NULL;
-}
-
-static void
-process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets)
-{
- BpEvents *evts = (BpEvents*)_evts;
- if (evts) {
- if (evts->is_ss)
- mono_component_debugger ()->mono_de_cancel_all_ss ();
- mono_wasm_fire_bp ();
- g_free (evts);
- }
-}
-
-static void
-no_seq_points_found (MonoMethod *method, int offset)
-{
- /*
- * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
- */
- PRINT_DEBUG_MSG (1, "Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
-}
-
#define DBG_NOT_SUSPENDED 1
-static int
-ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args)
-{
- PRINT_DEBUG_MSG (1, "ss_create_init_args\n");
- int dummy = 0;
- ss_req->start_sp = ss_req->last_sp = &dummy;
- compute_frames ();
- memset (ss_args, 0, sizeof (*ss_args));
-
- // This shouldn't happen - maybe should assert here ?
- if (frames->len == 0) {
- PRINT_DEBUG_MSG (1, "SINGLE STEPPING FOUND NO FRAMES");
- return DBG_NOT_SUSPENDED;
- }
-
- DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0);
- ss_req->start_method = ss_args->method = frame->method;
- gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->method, frame->native_offset, &ss_args->info, &ss_args->sp);
- if (!found_sp)
- no_seq_points_found (frame->method, frame->native_offset);
- g_assert (found_sp);
-
- ss_args->frames = (DbgEngineStackFrame**)frames->pdata;
- ss_args->nframes = frames->len;
- //XXX do sp
-
- return DE_ERR_NONE;
-}
-
-static void
-ss_args_destroy (SingleStepArgs *ss_args)
-{
- //nothing to do
-}
-
static int
handle_multiple_ss_requests (void) {
- mono_component_debugger ()->mono_de_cancel_all_ss ();
+ mono_de_cancel_all_ss ();
return 1;
}
@@ -421,17 +160,17 @@ mono_wasm_debugger_init (void)
.ss_calculate_framecount = ss_calculate_framecount,
.ensure_jit = ensure_jit,
.ensure_runtime_is_suspended = ensure_runtime_is_suspended,
- .get_this_async_id = get_this_async_id,
- .set_set_notification_for_wait_completion_flag = mono_component_debugger ()->set_set_notification_for_wait_completion_flag,
- .get_notify_debugger_of_wait_completion_method = mono_component_debugger ()->get_notify_debugger_of_wait_completion_method,
- .create_breakpoint_events = create_breakpoint_events,
- .process_breakpoint_events = process_breakpoint_events,
- .ss_create_init_args = ss_create_init_args,
- .ss_args_destroy = ss_args_destroy,
+ .get_this_async_id = mono_get_this_async_id,
+ .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag,
+ .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method,
+ .create_breakpoint_events = mono_dbg_create_breakpoint_events,
+ .process_breakpoint_events = mono_dbg_process_breakpoint_events,
+ .ss_create_init_args = mono_ss_create_init_args,
+ .ss_args_destroy = mono_ss_args_destroy,
.handle_multiple_ss_requests = handle_multiple_ss_requests,
};
- mono_debug_init (MONO_DEBUG_FORMAT_MONO);
+ mono_component_debugger ()->mono_debug_init (MONO_DEBUG_FORMAT_MONO);
mono_component_debugger ()->mono_de_init (&cbs);
mono_component_debugger ()->mono_de_set_log_level (log_level, stdout);
@@ -446,11 +185,16 @@ mono_wasm_debugger_init (void)
mono_profiler_set_domain_loaded_callback (prof, appdomain_load);
mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded);
- obj_to_objref = g_hash_table_new (NULL, NULL);
- objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_component_debugger ()->mono_debugger_free_objref);
+ mini_get_dbg_callbacks ()->handle_exception = mono_component_debugger ()->mono_debugger_agent_handle_exception;
+ mini_get_dbg_callbacks ()->user_break = mono_component_debugger ()->mono_dbg_debugger_agent_user_break;
+
+//debugger-agent initialization
+ DebuggerTransport trans;
+ trans.name = "buffer-wasm-communication";
+ trans.send = receive_debugger_agent_message;
- mono_component_debugger ()->handle_exception = handle_exception;
- mono_component_debugger ()->user_break = mono_wasm_user_break;
+ mono_component_debugger ()->register_transport (&trans);
+ mono_component_debugger ()->mono_init_debugger_agent_for_wasm (log_level);
}
MONO_API void
@@ -461,69 +205,6 @@ mono_wasm_enable_debugging (int debug_level)
log_level = debug_level;
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_pause_on_exceptions (int state)
-{
- pause_on_exc = state;
- PRINT_DEBUG_MSG (1, "setting pause on exception: %d\n", pause_on_exc);
- return 1;
-}
-
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_setup_single_step (int kind)
-{
- int nmodifiers = 1;
-
- PRINT_DEBUG_MSG (2, ">>>> mono_wasm_setup_single_step %d\n", kind);
- EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
- req->id = ++event_request_id;
- req->event_kind = EVENT_KIND_STEP;
- // DE doesn't care about suspend_policy
- // req->suspend_policy = SUSPEND_POLICY_ALL;
- req->nmodifiers = nmodifiers;
-
- StepSize size = STEP_SIZE_MIN;
-
- //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things.
- StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE);
- req->modifiers [0].data.filter = filter;
-
- StepDepth depth;
- switch (kind) {
- case 0: //into
- depth = STEP_DEPTH_INTO;
- break;
- case 1: //out
- depth = STEP_DEPTH_OUT;
- break;
- case 2: //over
- depth = STEP_DEPTH_OVER;
- break;
- default:
- g_error ("[dbg] unknown step kind %d", kind);
- }
-
- DbgEngineErrorCode err = mono_component_debugger ()->mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req);
- if (err != DE_ERR_NONE) {
- PRINT_DEBUG_MSG (1, "[dbg] Failed to setup single step request");
- }
- PRINT_DEBUG_MSG (1, "[dbg] single step is in place, now what?\n");
- SingleStepReq *ss_req = req->info;
- int isBPOnNativeCode = 0;
- if (ss_req && ss_req->bps) {
- GSList *l;
-
- for (l = ss_req->bps; l; l = l->next) {
- if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
- isBPOnNativeCode = 1;
- }
- }
- if (!isBPOnNativeCode) {
- mono_component_debugger ()->mono_de_cancel_all_ss ();
- }
- return isBPOnNativeCode;
-}
-
static void
assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly)
{
@@ -553,1403 +234,203 @@ assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly)
}
}
-static void
-handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame)
-{
- ERROR_DECL (error);
- const char *default_error_message = "Failed to get exception message.";
-
- PRINT_DEBUG_MSG (1, "handle exception - %d - %p - %p - %p\n", pause_on_exc, exc, throw_ctx, catch_ctx);
-
- //normal mono_runtime_try_invoke does not capture the exception and this is a temporary workaround.
- exception_on_runtime_invoke = (MonoObject*)exc;
-
- if (pause_on_exc == EXCEPTION_MODE_NONE)
- return;
- if (pause_on_exc == EXCEPTION_MODE_UNCAUGHT && catch_ctx != NULL)
- return;
-
- int obj_id = get_object_id ((MonoObject *)exc);
- char *error_message = mono_string_to_utf8_checked_internal (exc->message, error);
-
- const char *class_name = mono_class_full_name (mono_object_class (exc));
- PRINT_DEBUG_MSG (2, "handle exception - calling mono_wasm_fire_exc(): %d - message - %s, class_name: %s\n", obj_id, !is_ok (error) ? error_message : default_error_message, class_name);
-
- mono_wasm_fire_exception (obj_id, !is_ok (error) ? error_message : default_error_message, class_name, !catch_ctx);
- if (error_message != NULL)
- g_free (error_message);
-
- PRINT_DEBUG_MSG (2, "handle exception - done\n");
+void
+mono_wasm_single_step_hit (void)
+{
+ mono_component_debugger ()->mono_de_process_single_step (mono_component_debugger ()->mono_wasm_get_tls (), FALSE);
}
-
-EMSCRIPTEN_KEEPALIVE void
-mono_wasm_clear_all_breakpoints (void)
+void
+mono_wasm_breakpoint_hit (void)
{
- PRINT_DEBUG_MSG (1, "CLEAR BREAKPOINTS\n");
- mono_component_debugger ()->mono_de_clear_all_breakpoints ();
+ mono_component_debugger ()->mono_de_process_breakpoint (mono_component_debugger ()->mono_wasm_get_tls (), FALSE);
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset)
+static gboolean
+write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* variableValue)
{
- int i;
- ERROR_DECL (error);
- PRINT_DEBUG_MSG (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset);
-
-
- //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
- char *lookup_name = g_strdup (assembly_name);
- for (i = strlen (lookup_name) - 1; i >= 0; --i) {
- if (lookup_name [i] == '.') {
- lookup_name [i] = 0;
+ char* endptr;
+ errno = 0;
+ buffer_add_byte (buf, type);
+ switch (type) {
+ case MONO_TYPE_BOOLEAN:
+ if (!strcasecmp (variableValue, "True"))
+ buffer_add_int (buf, 1);
+ else if (!strcasecmp (variableValue, "False"))
+ buffer_add_int (buf, 0);
+ else
+ return FALSE;
+ break;
+ case MONO_TYPE_CHAR:
+ if (strlen (variableValue) > 1)
+ return FALSE;
+ buffer_add_int (buf, (variableValue [0]));
+ break;
+ case MONO_TYPE_I1: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= -128 && val <= 127)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
break;
}
+ case MONO_TYPE_U1: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= 0 && val <= 255)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
+ break;
+ }
+ case MONO_TYPE_I2: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= -32768 && val <= 32767)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
+ break;
+ }
+ case MONO_TYPE_U2: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= 0 && val <= 65535)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
+ break;
+ }
+ case MONO_TYPE_I4: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= -2147483648 && val <= 2147483647)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
+ break;
+ }
+ case MONO_TYPE_U4: {
+ intmax_t val = strtoimax (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ if (val >= 0 && val <= 4294967295)
+ buffer_add_int (buf, val);
+ else
+ return FALSE;
+ break;
+ }
+ case MONO_TYPE_I8: {
+ long long val = strtoll (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ buffer_add_long (buf, val);
+ break;
+ }
+ case MONO_TYPE_U8: {
+ long long val = strtoll (variableValue, &endptr, 10);
+ if (errno != 0)
+ return FALSE;
+ buffer_add_long (buf, val);
+ break;
+ }
+ case MONO_TYPE_R4: {
+ gfloat val = strtof (variableValue, &endptr);
+ if (errno != 0)
+ return FALSE;
+ buffer_add_int (buf, *((gint32*)(&val)));
+ break;
+ }
+ case MONO_TYPE_R8: {
+ gdouble val = strtof (variableValue, &endptr);
+ if (errno != 0)
+ return FALSE;
+ buffer_add_long (buf, *((guint64*)(&val)));
+ break;
+ }
+ default:
+ return FALSE;
}
-
- //resolve the assembly
- MonoImageOpenStatus status;
- MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
- MonoAssemblyByNameRequest byname_req;
- mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ());
- MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status);
- g_free (lookup_name);
- if (!assembly) {
- PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name);
- return -1;
- }
-
- mono_assembly_name_free_internal (aname);
-
- MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error);
- if (!method) {
- //FIXME don't swallow the error
- PRINT_DEBUG_MSG (1, "Could not find method due to %s\n", mono_error_get_message (error));
- mono_error_cleanup (error);
- return -1;
- }
-
- //FIXME right now none of the EventRequest fields are used by debugger-engine
- EventRequest *req = g_new0 (EventRequest, 1);
- req->id = ++event_request_id;
- req->event_kind = EVENT_KIND_BREAKPOINT;
- //DE doesn't care about suspend_policy
- // req->suspend_policy = SUSPEND_POLICY_ALL;
- req->nmodifiers = 0; //funny thing,
-
- // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset);
- MonoBreakpoint *bp = mono_component_debugger ()->mono_de_set_breakpoint (method, il_offset, req, error);
-
- if (!bp) {
- PRINT_DEBUG_MSG (1, "Could not set breakpoint to %s\n", mono_error_get_message (error));
- mono_error_cleanup (error);
- return 0;
- }
-
- PRINT_DEBUG_MSG (1, "NEW BP %p has id %d\n", req, req->id);
- return req->id;
-}
-
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_remove_breakpoint (int bp_id)
-{
- MonoBreakpoint *bp = mono_component_debugger ()->mono_de_get_breakpoint_by_id (bp_id);
- if (!bp)
- return 0;
-
- mono_component_debugger ()->mono_de_clear_breakpoint (bp);
- return 1;
+ return TRUE;
}
-void
-mono_wasm_single_step_hit (void)
+EMSCRIPTEN_KEEPALIVE void
+mono_wasm_set_is_debugger_attached (gboolean is_attached)
{
- mono_component_debugger ()->mono_de_process_single_step (NULL, FALSE);
+ mono_set_is_debugger_attached (is_attached);
+ if (is_attached && has_pending_lazy_loaded_assemblies)
+ {
+ GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies ();
+ for (int i = 0; i < assemblies->len; ++i) {
+ MonoAssembly *ass = (MonoAssembly*)g_ptr_array_index (assemblies, i);
+ assembly_loaded (NULL, ass);
+ }
+ g_ptr_array_free (assemblies, TRUE);
+ has_pending_lazy_loaded_assemblies = FALSE;
+ }
}
-void
-mono_wasm_breakpoint_hit (void)
+EMSCRIPTEN_KEEPALIVE gboolean
+mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue)
{
- mono_component_debugger ()->mono_de_process_breakpoint (NULL, FALSE);
- // mono_wasm_fire_bp ();
+ MdbgProtBuffer bufWithParms;
+ buffer_init (&bufWithParms, 128);
+ m_dbgprot_buffer_add_data (&bufWithParms, data, size);
+ if (!write_value_to_buffer(&bufWithParms, valtype, newvalue)) {
+ EM_ASM ({
+ MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3);
+ }, 0, id, 0, 0);
+ return TRUE;
+ }
+ mono_wasm_send_dbg_command(id, command_set, command, bufWithParms.buf, m_dbgprot_buffer_len(&bufWithParms));
+ buffer_free (&bufWithParms);
+ return TRUE;
}
-void
-mono_wasm_user_break (void)
+EMSCRIPTEN_KEEPALIVE gboolean
+mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size)
{
- mono_wasm_fire_bp ();
+ ss_calculate_framecount (NULL, NULL, TRUE, NULL, NULL);
+ MdbgProtBuffer buf;
+ buffer_init (&buf, 128);
+ gboolean no_reply;
+ MdbgProtErrorCode error = 0;
+ if (command_set == MDBGPROT_CMD_SET_VM && command == MDBGPROT_CMD_VM_INVOKE_METHOD )
+ {
+ DebuggerTlsData* tls = mono_component_debugger ()->mono_wasm_get_tls ();
+ InvokeData invoke_data;
+ memset (&invoke_data, 0, sizeof (InvokeData));
+ invoke_data.endp = data + size;
+ error = mono_do_invoke_method (tls, &buf, &invoke_data, data, &data);
+ }
+ else
+ error = mono_component_debugger ()->mono_process_dbg_packet (id, command_set, command, &no_reply, data, data + size, &buf);
+ EM_ASM ({
+ MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3);
+ }, error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf);
+
+ buffer_free (&buf);
+ return TRUE;
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_current_bp_id (void)
+static gboolean
+receive_debugger_agent_message (void *data, int len)
{
- PRINT_DEBUG_MSG (2, "COMPUTING breakpoint ID\n");
- //FIXME handle compiled case
-
- /* Interpreter */
- MonoLMF *lmf = mono_get_lmf ();
-
- g_assert (((guint64)lmf->previous_lmf) & 2);
- MonoLMFExt *ext = (MonoLMFExt*)lmf;
-
- g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
- MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
- MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
- guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame);
-
- g_assert (ji && !ji->is_trampoline);
- MonoMethod *method = jinfo_get_method (ji);
-
- /* Compute the native offset of the breakpoint from the ip */
- guint32 native_offset = ip - (guint8*)ji->code_start;
-
- MonoSeqPointInfo *info = NULL;
- SeqPoint sp;
- gboolean found_sp = mono_find_prev_seq_point_for_native_offset (method, native_offset, &info, &sp);
- if (!found_sp)
- PRINT_DEBUG_MSG (1, "Could not find SP\n");
-
-
- GPtrArray *bp_reqs = g_ptr_array_new ();
- mono_component_debugger ()->mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs);
-
- if (bp_reqs->len == 0) {
- PRINT_DEBUG_MSG (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset);
- return -1;
- }
-
- if (bp_reqs->len > 1)
- PRINT_DEBUG_MSG (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len);
-
- EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0);
- g_ptr_array_free (bp_reqs, TRUE);
-
- PRINT_DEBUG_MSG (1, "Found BP %p with id %d\n", evt, evt->id);
- return evt->id;
-}
-
-static MonoObject*
-get_object_from_id (int objectId)
-{
- ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
- if (!ref) {
- PRINT_DEBUG_MSG (2, "get_object_from_id !ref: %d\n", objectId);
- return NULL;
- }
-
- MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
- if (!obj)
- PRINT_DEBUG_MSG (2, "get_object_from_id !obj: %d\n", objectId);
-
- return obj;
-}
-
-static gboolean
-list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
-{
- SeqPoint sp;
- MonoMethod *method;
- char *method_full_name;
-
- int* frame_id_p = (int*)data;
- (*frame_id_p)++;
-
- //skip wrappers
- if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
- return FALSE;
-
- if (info->ji)
- method = jinfo_get_method (info->ji);
- else
- method = info->method;
-
- if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
- return FALSE;
-
- PRINT_DEBUG_MSG (2, "list_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type);
-
- if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp))
- PRINT_DEBUG_MSG (2, "list_frames: Failed to lookup sequence point. method: %s, native_offset: %d\n", method->name, info->native_offset);
-
- method_full_name = mono_method_full_name (method, FALSE);
- while (method->is_inflated)
- method = ((MonoMethodInflated*)method)->declaring;
-
- char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name);
- inplace_tolower (assembly_name);
-
- PRINT_DEBUG_MSG (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
- mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), *frame_id_p, assembly_name, method_full_name);
-
- g_free (assembly_name);
-
- return FALSE;
-}
-
-EMSCRIPTEN_KEEPALIVE void
-mono_wasm_enum_frames (void)
-{
- int frame_id = -1;
- mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, &frame_id);
-}
-
-static char*
-invoke_to_string (const char *class_name, MonoClass *klass, gpointer addr)
-{
- MonoObject *exc;
- MonoString *mstr;
- char *ret_str;
- ERROR_DECL (error);
- MonoObject *obj;
-
- // TODO: this is for a specific use case right now,
- // (invoke ToString() get a preview/description for *some* types)
- // and we don't want to report errors for that.
- if (m_class_is_valuetype (klass)) {
- MonoMethod *method;
-
- MONO_STATIC_POINTER_INIT (MonoMethod, to_string)
- to_string = mono_class_get_method_from_name_checked (mono_get_object_class (), "ToString", 0, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC, error);
- mono_error_assert_ok (error);
- MONO_STATIC_POINTER_INIT_END (MonoMethod, to_string)
-
- method = mono_class_get_virtual_method (klass, to_string, error);
- if (!method)
- return NULL;
-
- MonoString *mstr = (MonoString*) mono_runtime_try_invoke_internal (method, addr , NULL, &exc, error);
- if (exc || !is_ok (error)) {
- PRINT_DEBUG_MSG (1, "Failed to invoke ToString for %s\n", class_name);
- return NULL;
- }
-
- return mono_string_to_utf8_checked_internal (mstr, error);
- }
-
- obj = *(MonoObject**)addr;
- if (!obj)
- return NULL;
-
- mstr = mono_object_try_to_string (obj, &exc, error);
- if (exc || !is_ok (error))
- return NULL;
-
- ret_str = mono_string_to_utf8_checked_internal (mstr, error);
- if (!is_ok (error))
- return NULL;
-
- return ret_str;
-}
-
-static char*
-get_to_string_description (const char* class_name, MonoClass *klass, gpointer addr)
-{
- if (!class_name || !klass || !addr)
- return NULL;
-
- if (strcmp (class_name, "System.Guid") == 0)
- return mono_guid_to_string (addr);
-
- for (int i = 0; i < G_N_ELEMENTS (to_string_as_descr_names); i ++) {
- if (strcmp (to_string_as_descr_names [i], class_name) == 0) {
- return invoke_to_string (class_name, klass, addr);
- }
- }
-
- return NULL;
-}
-
-typedef struct {
- int cur_frame;
- int target_frame;
- int len;
- int *pos;
- gboolean found;
-} FrameDescData;
-
-
-typedef struct {
- int cur_frame;
- int target_frame;
- int pos;
- const char* new_value;
- gboolean found;
- gboolean error;
-} SetVariableValueData;
-
-/*
- * this returns a string formatted like
- *
- * :[]:
- *
- * .. which is consumed by `mono_wasm_add_func_var`. It is used for
- * generating this for the delegate, and it's target.
- */
-static char*
-mono_method_to_desc_for_js (MonoMethod *method, gboolean include_namespace)
-{
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- char *ret_desc = mono_type_full_name (sig->ret);
- char *args_desc = mono_signature_get_desc (sig, include_namespace);
-
- char *sig_desc = g_strdup_printf ("%s:%s:%s", ret_desc, args_desc, method->name);
-
- g_free (ret_desc);
- g_free (args_desc);
- return sig_desc;
-}
-
-static guint64
-read_enum_value (const char *mem, int type)
-{
- switch (type) {
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_U1:
- return *(guint8*)mem;
- case MONO_TYPE_I1:
- return *(gint8*)mem;
- case MONO_TYPE_CHAR:
- case MONO_TYPE_U2:
- return read16 (mem);
- case MONO_TYPE_I2:
- return (gint16) read16 (mem);
- case MONO_TYPE_U4:
- case MONO_TYPE_R4:
- return read32 (mem);
- case MONO_TYPE_I4:
- return (gint32) read32 (mem);
- case MONO_TYPE_U8:
- case MONO_TYPE_I8:
- case MONO_TYPE_R8:
- return read64 (mem);
- case MONO_TYPE_U:
- case MONO_TYPE_I:
-#if SIZEOF_REGISTER == 8
- return read64 (mem);
-#else
- return read32 (mem);
-#endif
- default:
- g_assert_not_reached ();
- }
- return 0;
-}
-
-static gboolean
-nullable_try_get_value (guint8 *nullable, MonoClass *klass, gpointer* out_value)
-{
- mono_class_setup_fields (klass);
- g_assert (m_class_is_fields_inited (klass));
-
- *out_value = NULL;
- MonoClassField *klass_fields = m_class_get_fields (klass);
- gpointer addr_for_has_value = mono_vtype_get_field_addr (nullable, &klass_fields[0]);
- if (0 == *(guint8*)addr_for_has_value)
- return FALSE;
-
- *out_value = mono_vtype_get_field_addr (nullable, &klass_fields[1]);
- return TRUE;
-}
-
-static gboolean
-describe_value(MonoType * type, gpointer addr, int gpflags)
-{
- ERROR_DECL (error);
- switch (type->type) {
- case MONO_TYPE_BOOLEAN:
- mono_wasm_add_typed_value ("bool", NULL, *(gint8*)addr);
- break;
- case MONO_TYPE_I1:
- mono_wasm_add_typed_value ("number", NULL, *(gint8*)addr);
- break;
- case MONO_TYPE_U1:
- mono_wasm_add_typed_value ("number", NULL, *(guint8*)addr);
- break;
- case MONO_TYPE_CHAR:
- mono_wasm_add_typed_value ("char", NULL, *(guint16*)addr);
- break;
- case MONO_TYPE_U2:
- mono_wasm_add_typed_value ("number", NULL, *(guint16*)addr);
- break;
- case MONO_TYPE_I2:
- mono_wasm_add_typed_value ("number", NULL, *(gint16*)addr);
- break;
- case MONO_TYPE_I4:
- case MONO_TYPE_I:
- mono_wasm_add_typed_value ("number", NULL, *(gint32*)addr);
- break;
- case MONO_TYPE_U4:
- case MONO_TYPE_U:
- mono_wasm_add_typed_value ("number", NULL, *(guint32*)addr);
- break;
- case MONO_TYPE_I8:
- mono_wasm_add_typed_value ("number", NULL, *(gint64*)addr);
- break;
- case MONO_TYPE_U8:
- mono_wasm_add_typed_value ("number", NULL, *(guint64*)addr);
- break;
- case MONO_TYPE_R4:
- mono_wasm_add_typed_value ("number", NULL, *(float*)addr);
- break;
- case MONO_TYPE_R8:
- mono_wasm_add_typed_value ("number", NULL, *(double*)addr);
- break;
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR: {
- char *class_name = mono_type_full_name (type);
- const void *val = *(const void **)addr;
- char *descr = g_strdup_printf ("(%s) %p", class_name, val);
-
- EM_ASM ({
- MONO.mono_wasm_add_typed_value ('pointer', $0, { ptr_addr: $1, klass_addr: $2 });
- }, descr, val ? addr : 0, val ? mono_class_from_mono_type_internal (type) : 0);
-
- g_free (descr);
- g_free (class_name);
- break;
- }
-
- case MONO_TYPE_STRING: {
- MonoString *str_obj = *(MonoString **)addr;
- if (!str_obj) {
- mono_wasm_add_typed_value ("string", NULL, 0);
- } else {
- char *str = mono_string_to_utf8_checked_internal (str_obj, error);
- mono_error_assert_ok (error); /* FIXME report error */
- mono_wasm_add_typed_value ("string", str, 0);
- g_free (str);
- }
- break;
- }
-
- case MONO_TYPE_OBJECT: {
- MonoObject *obj = *(MonoObject**)addr;
- if (!obj) {
- mono_wasm_add_obj_var ("object", NULL, 0);
- break;
- }
- MonoClass *klass = obj->vtable->klass;
- if (!klass) {
- // boxed null
- mono_wasm_add_obj_var ("object", NULL, 0);
- break;
- }
-
- type = m_class_get_byval_arg (klass);
- if (type->type == MONO_TYPE_OBJECT) {
- mono_wasm_add_obj_var ("object", "object", get_object_id (obj));
- break;
- }
-
- // Boxed valuetype
- if (m_class_is_valuetype (klass))
- addr = mono_object_unbox_internal (obj);
-
- return describe_value (type, addr, gpflags);
- }
-
- case MONO_TYPE_GENERICINST: {
- MonoClass *klass = mono_class_from_mono_type_internal (type);
- if (mono_class_is_nullable (klass)) {
- MonoType *targ = type->data.generic_class->context.class_inst->type_argv [0];
-
- gpointer nullable_value = NULL;
- if (nullable_try_get_value (addr, klass, &nullable_value)) {
- return describe_value (targ, nullable_value, gpflags);
- } else {
- char* class_name = mono_type_full_name (type);
- mono_wasm_add_obj_var (class_name, NULL, 0);
- g_free (class_name);
- break;
- }
- }
-
- if (mono_type_generic_inst_is_valuetype (type))
- goto handle_vtype;
- /*
- * else fallthrough
- */
- }
-
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_CLASS: {
- MonoObject *obj = *(MonoObject**)addr;
- if (!obj) {
- char *class_name = mono_type_full_name (type);
- mono_wasm_add_func_var (class_name, NULL, 0);
- g_free (class_name);
- return TRUE;
- }
- MonoClass *klass = type->data.klass;
-
- if (m_class_is_valuetype (mono_object_class (obj))) {
- addr = mono_object_unbox_internal (obj);
- type = m_class_get_byval_arg (mono_object_class (obj));
- goto handle_vtype;
- }
-
- char *class_name = mono_type_full_name (type);
- int obj_id = get_object_id (obj);
-
- if (type-> type == MONO_TYPE_ARRAY || type->type == MONO_TYPE_SZARRAY) {
- MonoArray *array = (MonoArray *)obj;
- EM_ASM ({
- MONO.mono_wasm_add_typed_value ('array', $0, { objectId: $1, length: $2 });
- }, class_name, obj_id, mono_array_length_internal (array));
- } else if (m_class_is_delegate (klass) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) {
- MonoMethod *method;
-
- if (type->type == MONO_TYPE_GENERICINST)
- klass = type->data.generic_class->container_class;
-
- method = mono_get_delegate_invoke_internal (klass);
- if (!method) {
- mono_wasm_add_func_var (class_name, NULL, -1);
- } else {
- MonoMethod *tm = ((MonoDelegate *)obj)->method;
- char *tm_desc = NULL;
- if (tm)
- tm_desc = mono_method_to_desc_for_js (tm, FALSE);
-
- mono_wasm_add_func_var (class_name, tm_desc, obj_id);
- g_free (tm_desc);
- }
- } else {
- char *to_string_val = get_to_string_description (class_name, klass, addr);
- mono_wasm_add_obj_var (class_name, to_string_val, obj_id);
- g_free (to_string_val);
- }
- g_free (class_name);
- break;
- }
-
- handle_vtype:
- case MONO_TYPE_VALUETYPE: {
- g_assert (addr);
- MonoClass *klass = mono_class_from_mono_type_internal (type);
- char *class_name = mono_type_full_name (type);
-
- if (m_class_is_enumtype (klass)) {
- MonoClassField *field;
- gpointer iter = NULL;
- const char *p;
- MonoTypeEnum def_type;
- guint64 field_value;
- guint64 value__ = 0xDEAD;
- GString *enum_members = g_string_new ("");
- int base_type = mono_class_enum_basetype_internal (klass)->type;
-
- while ((field = mono_class_get_fields_internal (klass, &iter))) {
- if (strcmp ("value__", mono_field_get_name (field)) == 0) {
- value__ = read_enum_value (mono_vtype_get_field_addr (addr, field), base_type);
- continue;
- }
-
- if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
- continue;
- if (mono_field_is_deleted (field))
- continue;
-
- p = mono_class_get_field_default_value (field, &def_type);
- /* this is to correctly increment `p` in the blob */
- /* len = */ mono_metadata_decode_blob_size (p, &p);
-
- field_value = read_enum_value (p, base_type);
-
- g_string_append_printf (enum_members, ",%s:%llu", mono_field_get_name (field), field_value);
- }
-
- mono_wasm_add_enum_var (class_name, enum_members->str, value__);
- g_string_free (enum_members, TRUE);
- } else {
- char *to_string_val = get_to_string_description (class_name, klass, addr);
-
- if (gpflags & GPFLAG_EXPAND_VALUETYPES) {
- int32_t size = mono_class_value_size (klass, NULL);
- void *value_buf = g_malloc0 (size);
- mono_value_copy_internal (value_buf, addr, klass);
-
- EM_ASM ({
- MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2, value_addr: $3, value_size: $4, klass: $5 });
- }, "begin_vt", class_name, to_string_val, value_buf, size, klass);
-
- g_free (value_buf);
-
- // FIXME: isAsyncLocalThis
- describe_object_properties_for_klass (addr, klass, FALSE, gpflags);
- mono_wasm_add_typed_value ("end_vt", NULL, 0);
- } else {
- EM_ASM ({
- MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2 });
- }, "unexpanded_vt", class_name, to_string_val);
- }
- g_free (to_string_val);
- }
- g_free (class_name);
- break;
- }
- default: {
- char *type_name = mono_type_full_name (type);
- char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
- mono_wasm_add_typed_value ("string", msg, 0);
- g_free (msg);
- g_free (type_name);
- }
- }
- return TRUE;
-}
-
-static gboolean
-are_getters_allowed (const char *class_name)
-{
- for (int i = 0; i < G_N_ELEMENTS (all_getters_allowed_class_names); i ++) {
- if (strcmp (class_name, all_getters_allowed_class_names [i]) == 0)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-invoke_and_describe_getter_value (MonoObject *obj, MonoProperty *p)
-{
- ERROR_DECL (error);
- MonoObject *res;
- MonoObject *exc;
-
- MonoMethodSignature *sig = mono_method_signature_internal (p->get);
-
- res = mono_runtime_try_invoke_internal (p->get, obj, NULL, &exc, error);
- if (!is_ok (error) && exc == NULL)
- exc = (MonoObject *) mono_error_convert_to_exception (error);
- if (exc)
- {
- const char *class_name = mono_class_full_name (mono_object_class (exc));
- ERROR_DECL (local_error);
- char *str = mono_string_to_utf8_checked_internal (((MonoException*)exc)->message, local_error);
- mono_error_assert_ok (local_error); /* FIXME report error */
- char *msg = g_strdup_printf("%s: %s", class_name, str);
- mono_wasm_add_typed_value ("string", msg, 0);
- g_free (msg);
- return TRUE;
- }
- else if (!res || !m_class_is_valuetype (mono_object_class (res)))
- return describe_value (sig->ret, &res, GPFLAG_EXPAND_VALUETYPES);
- else
- return describe_value (sig->ret, mono_object_unbox_internal (res), GPFLAG_EXPAND_VALUETYPES);
-}
-
-static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error)
-{
- exception_on_runtime_invoke = NULL;
- MonoObject* res = mono_runtime_try_invoke (method, obj, params, exc, error);
- if (exception_on_runtime_invoke != NULL)
- *exc = exception_on_runtime_invoke;
- exception_on_runtime_invoke = NULL;
- return res;
-}
-
-static void
-describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags)
-{
- MonoClassField *f;
- MonoProperty *p;
- MonoMethodSignature *sig;
- gboolean is_valuetype;
- int pnum;
- char *klass_name;
- gboolean auto_invoke_getters;
- gboolean is_own;
- gboolean only_backing_fields;
-
- g_assert (klass);
- MonoClass *start_klass = klass;
-
- only_backing_fields = gpflags & GPFLAG_ACCESSORS_ONLY;
- is_valuetype = m_class_is_valuetype(klass);
- if (is_valuetype)
- gpflags |= GPFLAG_EXPAND_VALUETYPES;
-
-handle_parent:
- is_own = (start_klass == klass);
- klass_name = mono_class_full_name (klass);
- gpointer iter = NULL;
- while (obj && (f = mono_class_get_fields_internal (klass, &iter))) {
- if (isAsyncLocalThis && f->name[0] == '<' && f->name[1] == '>') {
- if (g_str_has_suffix (f->name, "__this")) {
- mono_wasm_add_properties_var ("this", f->offset);
- gpointer field_value = (guint8*)obj + f->offset;
-
- describe_value (f->type, field_value, gpflags);
- }
-
- continue;
- }
- if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
- continue;
- if (mono_field_is_deleted (f))
- continue;
-
- if (only_backing_fields && !g_str_has_suffix(f->name, "k__BackingField"))
- continue;
-
- EM_ASM ({
- MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 });
- }, f->name, f->offset, is_own, f->type->attrs, klass_name);
-
- gpointer field_addr;
- if (is_valuetype)
- field_addr = mono_vtype_get_field_addr (obj, f);
- else
- field_addr = (guint8*)obj + f->offset;
-
- describe_value (f->type, field_addr, gpflags);
- }
-
- auto_invoke_getters = are_getters_allowed (klass_name);
- iter = NULL;
- pnum = 0;
- while ((p = mono_class_get_properties (klass, &iter))) {
- if (p->get->name) { //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug
- if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>')))
- continue;
-
- sig = mono_method_signature_internal (p->get);
- if (sig->param_count != 0) {
- // getters with params are not shown
- continue;
- }
-
- if (p->get->flags & METHOD_ATTRIBUTE_STATIC)
- continue;
-
- EM_ASM ({
- MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 });
- }, p->name, pnum, is_own, p->attrs, klass_name);
-
- gboolean vt_self_type_getter = is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass;
- if (auto_invoke_getters && !vt_self_type_getter) {
- invoke_and_describe_getter_value (obj, p);
- } else {
- // not allowed to call the getter here
- char *ret_class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret));
-
- mono_wasm_add_typed_value ("getter", ret_class_name, -1);
-
- g_free (ret_class_name);
- continue;
- }
- }
- pnum ++;
- }
-
- g_free (klass_name);
-
- // ownProperties
- // Note: ownProperties should mean that we return members of the klass itself,
- // but we are going to ignore that here, because otherwise vscode/chrome don't
- // seem to ask for inherited fields at all.
- // if (!is_valuetype && !(gpflags & GPFLAG_OWN_PROPERTIES) && (klass = m_class_get_parent (klass)))
- if (!is_valuetype && (klass = m_class_get_parent (klass)))
- goto handle_parent;
-}
-
-/*
- * We return a `Target` property only for now.
- * In future, we could add a `MethodInfo` too.
- */
-static gboolean
-describe_delegate_properties (MonoObject *obj)
-{
- MonoClass *klass = mono_object_class(obj);
- if (!m_class_is_delegate (klass))
- return FALSE;
-
- // Target, like in VS - what is this field supposed to be, anyway??
- MonoMethod *tm = ((MonoDelegate *)obj)->method;
- char * sig_desc = mono_method_to_desc_for_js (tm, FALSE);
-
- mono_wasm_add_properties_var ("Target", -1);
- mono_wasm_add_func_var (NULL, sig_desc, -1);
-
- g_free (sig_desc);
- return TRUE;
-}
-
-static gboolean
-describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, int gpflags)
-{
- PRINT_DEBUG_MSG (2, "describe_object_properties %llu, gpflags: %d\n", objectId, gpflags);
-
- MonoObject *obj = get_object_from_id (objectId);
- if (!obj)
- return FALSE;
-
- if (m_class_is_delegate (mono_object_class (obj))) {
- // delegates get the same id format as regular objects
- describe_delegate_properties (obj);
- } else {
- describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, gpflags);
- }
-
- return TRUE;
-}
-
-static gboolean
-invoke_getter (void *obj_or_value, MonoClass *klass, const char *name)
-{
- if (!obj_or_value || !klass || !name) {
- PRINT_DEBUG_MSG (2, "invoke_getter: none of the arguments can be null");
- return FALSE;
- }
-
- gpointer iter;
-handle_parent:
- iter = NULL;
- MonoProperty *p;
- while ((p = mono_class_get_properties (klass, &iter))) {
- //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug
- if (!p->get->name || strcasecmp (p->name, name) != 0)
- continue;
-
- invoke_and_describe_getter_value (obj_or_value, p);
- return TRUE;
- }
-
- if ((klass = m_class_get_parent(klass)))
- goto handle_parent;
-
- return FALSE;
-}
-
-static gboolean
-describe_array_values (guint64 objectId, int startIdx, int count, int gpflags)
-{
- if (count == 0)
- return TRUE;
-
- int esize;
- gpointer elem;
- MonoArray *arr = (MonoArray*) get_object_from_id (objectId);
- if (!arr)
- return FALSE;
-
- MonoClass *klass = mono_object_class (arr);
- MonoTypeEnum type = m_class_get_byval_arg (klass)->type;
- if (type != MONO_TYPE_SZARRAY && type != MONO_TYPE_ARRAY) {
- PRINT_DEBUG_MSG (1, "describe_array_values: object is not an array. type: 0x%x\n", type);
- return FALSE;
- }
-
- int len = arr->max_length;
- if (len == 0 && startIdx == 0 && count <= 0) {
- // Nothing to do
- return TRUE;
- }
-
- if (startIdx < 0 || (len > 0 && startIdx >= len)) {
- PRINT_DEBUG_MSG (1, "describe_array_values: invalid startIdx (%d) for array of length %d\n", startIdx, len);
- return FALSE;
- }
-
- if (count > 0 && (startIdx + count) > len) {
- PRINT_DEBUG_MSG (1, "describe_array_values: invalid count (%d) for startIdx: %d, and array of length %d\n", count, startIdx, len);
- return FALSE;
- }
-
- esize = mono_array_element_size (klass);
- int endIdx = count < 0 ? len : startIdx + count;
-
- for (int i = startIdx; i < endIdx; i ++) {
- mono_wasm_add_array_item(i);
- elem = (gpointer*)((char*)arr->vector + (i * esize));
- describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, gpflags);
- }
- return TRUE;
-}
-
-static void
-describe_async_method_locals (InterpFrame *frame, MonoMethod *method)
-{
- //Async methods are special in the way that local variables can be lifted to generated class fields
- gpointer addr = NULL;
- if (mono_debug_lookup_method_async_debug_info (method)) {
- addr = mini_get_interp_callbacks ()->frame_get_this (frame);
- MonoObject *obj = *(MonoObject**)addr;
- int objId = get_object_id (obj);
- mono_wasm_set_is_async_method (objId);
- describe_object_properties (objId, TRUE, GPFLAG_NONE);
- }
-}
-
-static void
-describe_non_async_this (InterpFrame *frame, MonoMethod *method)
-{
- gpointer addr = NULL;
- if (mono_debug_lookup_method_async_debug_info (method))
- return;
-
- if (mono_method_signature_internal (method)->hasthis) {
- addr = mini_get_interp_callbacks ()->frame_get_this (frame);
- MonoObject *obj = *(MonoObject**)addr;
- MonoClass *klass = method->klass;
- MonoType *type = m_class_get_byval_arg (method->klass);
-
- mono_wasm_add_properties_var ("this", -1);
-
- if (m_class_is_valuetype (klass)) {
- describe_value (type, obj, GPFLAG_EXPAND_VALUETYPES);
- } else {
- // this is an object, and we can retrieve the valuetypes in it later
- // through the object id
- describe_value (type, addr, GPFLAG_NONE);
- }
- }
-}
-
-static gboolean
-describe_variable (InterpFrame *frame, MonoMethod *method, MonoMethodHeader *header, int pos, int gpflags)
-{
- MonoType *type = NULL;
- gpointer addr = NULL;
- if (pos < 0) {
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- pos = -pos - 1;
-
- if (pos >= sig->param_count) {
- PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access param indexed %d, but the method (%s) has only %d params\n", pos, method->name, sig->param_count);
- return FALSE;
- }
-
- type = sig->params [pos];
- addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
- } else {
- if (pos >= header->num_locals) {
- PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access local indexed %d, but the method (%s) has only %d locals\n", pos, method->name, header->num_locals);
- return FALSE;
- }
-
- type = header->locals [pos];
- addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos);
- }
-
- PRINT_DEBUG_MSG (2, "adding val %p type 0x%x %s\n", addr, type->type, mono_type_full_name (type));
-
- return describe_value(type, addr, gpflags);
-}
-
-static gboolean
-decode_value (MonoType *t, guint8 *addr, const char* variableValue)
-{
- char* endptr;
- errno = 0;
- switch (t->type) {
- case MONO_TYPE_BOOLEAN:
- if (!strcasecmp (variableValue, "True"))
- *(guint8*)addr = 1;
- else if (!strcasecmp (variableValue, "False"))
- *(guint8*)addr = 0;
- else
- return FALSE;
- break;
- case MONO_TYPE_CHAR:
- if (strlen (variableValue) > 1)
- return FALSE;
- *(gunichar2*)addr = variableValue [0];
- break;
- case MONO_TYPE_I1: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= -128 && val <= 127)
- *(gint8*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_U1: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= 0 && val <= 255)
- *(guint8*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_I2: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= -32768 && val <= 32767)
- *(gint16*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_U2: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= 0 && val <= 65535)
- *(guint16*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_I4: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= -2147483648 && val <= 2147483647)
- *(gint32*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_U4: {
- intmax_t val = strtoimax (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- if (val >= 0 && val <= 4294967295)
- *(guint32*)addr = val;
- else
- return FALSE;
- break;
- }
- case MONO_TYPE_I8: {
- long long val = strtoll (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- *(gint64*)addr = val;
- break;
- }
- case MONO_TYPE_U8: {
- long long val = strtoll (variableValue, &endptr, 10);
- if (errno != 0)
- return FALSE;
- *(guint64*)addr = val;
- break;
- }
- case MONO_TYPE_R4: {
- gfloat val = strtof (variableValue, &endptr);
- if (errno != 0)
- return FALSE;
- *(gfloat*)addr = val;
- break;
- }
- case MONO_TYPE_R8: {
- gdouble val = strtof (variableValue, &endptr);
- if (errno != 0)
- return FALSE;
- *(gdouble*)addr = val;
- break;
- }
- default:
- return FALSE;
- }
- return TRUE;
-}
-
-static gboolean
-set_variable_value_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
-{
- ERROR_DECL (error);
- SetVariableValueData *data = (SetVariableValueData*)ud;
- gboolean is_arg = FALSE;
- MonoType *t = NULL;
- guint8 *val_buf = NULL;
-
- ++data->cur_frame;
-
- //skip wrappers
- if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
- return FALSE;
- }
-
- if (data->cur_frame != data->target_frame)
- return FALSE;
-
- data->found = TRUE;
-
- InterpFrame *frame = (InterpFrame*)info->interp_frame;
- MonoMethod *method = frame->imethod->method;
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- MonoMethodHeader *header = mono_method_get_header_checked (method, error);
-
- if (!header) {
- mono_error_cleanup(error);
- data->error = TRUE;
- return TRUE;
- }
-
- if (!sig)
- goto exit_with_error;
-
- int pos = data->pos;
-
- if (pos < 0) {
- pos = - pos - 1;
- if (pos >= sig->param_count)
- goto exit_with_error;
- is_arg = TRUE;
- t = sig->params [pos];
- }
- else {
- if (pos >= header->num_locals)
- goto exit_with_error;
- t = header->locals [pos];
- }
-
- guint8 *addr;
- if (is_arg)
- addr = (guint8*)mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
- else
- addr = (guint8*)mini_get_interp_callbacks ()->frame_get_local (frame, pos);
-
- val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (t)));
-
- if (!decode_value(t, val_buf, data->new_value))
- goto exit_with_error;
-
- DbgEngineErrorCode errorCode = mono_component_debugger ()->mono_de_set_interp_var (t, addr, val_buf);
- if (errorCode != ERR_NONE) {
- goto exit_with_error;
- }
-
- mono_metadata_free_mh (header);
- return TRUE;
-
-exit_with_error:
- data->error = TRUE;
- mono_metadata_free_mh (header);
- return TRUE;
-}
-
-static gboolean
-describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
-{
- ERROR_DECL (error);
- FrameDescData *data = (FrameDescData*)ud;
-
- ++data->cur_frame;
-
- //skip wrappers
- if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
- return FALSE;
- }
-
- if (data->cur_frame != data->target_frame)
- return FALSE;
-
- data->found = TRUE;
-
- InterpFrame *frame = (InterpFrame*)info->interp_frame;
- g_assert (frame);
- MonoMethod *method = frame->imethod->method;
- g_assert (method);
-
- MonoMethodHeader *header = mono_method_get_header_checked (method, error);
- mono_error_assert_ok (error); /* FIXME report error */
-
- for (int i = 0; i < data->len; i++)
- {
- if (!describe_variable (frame, method, header, data->pos[i], GPFLAG_EXPAND_VALUETYPES))
- mono_wasm_add_typed_value("symbol", "", 0);
- }
-
- describe_async_method_locals (frame, method);
- describe_non_async_this (frame, method);
-
- mono_metadata_free_mh (header);
- return TRUE;
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value)
-{
- if (scope < 0)
- return FALSE;
-
- SetVariableValueData data;
- data.target_frame = scope;
- data.cur_frame = -1;
- data.pos = index;
- data.found = FALSE;
- data.new_value = value;
- data.error = FALSE;
-
- mono_walk_stack_with_ctx (set_variable_value_on_frame, NULL, MONO_UNWIND_NONE, &data);
- return !data.error;
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass)
-{
- MonoType *type = m_class_get_byval_arg (klass);
- if (type->type != MONO_TYPE_PTR && type->type != MONO_TYPE_FNPTR) {
- PRINT_DEBUG_MSG (2, "BUG: mono_wasm_get_deref_ptr_value: Expected to get a ptr type, but got 0x%x\n", type->type);
- return FALSE;
- }
-
- mono_wasm_add_properties_var ("deref", -1);
- return describe_value (type->data.type, value_addr, GPFLAG_EXPAND_VALUETYPES);
-}
-
-//FIXME this doesn't support getting the return value pseudo-var
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_get_local_vars (int scope, int* pos, int len)
-{
- if (scope < 0)
- return FALSE;
-
- FrameDescData data;
- data.target_frame = scope;
- data.cur_frame = -1;
- data.len = len;
- data.pos = pos;
- data.found = FALSE;
-
- mono_walk_stack_with_ctx (describe_variables_on_frame, NULL, MONO_UNWIND_NONE, &data);
-
- return data.found;
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_get_object_properties (int object_id, int gpflags)
-{
- PRINT_DEBUG_MSG (2, "getting properties of object %d, gpflags: %d\n", object_id, gpflags);
-
- return describe_object_properties (object_id, FALSE, gpflags);
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags)
-{
- PRINT_DEBUG_MSG (2, "getting array values %d, startIdx: %d, count: %d, gpflags: 0x%x\n", object_id, start_idx, count, gpflags);
-
- return describe_array_values (object_id, start_idx, count, gpflags);
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_invoke_getter_on_object (int object_id, const char* name)
-{
- MonoObject *obj = get_object_from_id (object_id);
- if (!obj)
- return FALSE;
-
- return invoke_getter (obj, mono_object_class (obj), name);
-}
-
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_set_value_on_object (int object_id, const char* name, const char* value)
-{
- PRINT_DEBUG_MSG (1, "mono_wasm_set_value_on_object %d, name: %s, value: %s\n", object_id, name, value);
- MonoObject *obj = get_object_from_id (object_id);
-
- if (!obj || !name) {
- PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object: none of the arguments can be null");
- return FALSE;
- }
- MonoClass* klass = mono_object_class (obj);
-
- gpointer iter;
-handle_parent:
- iter = NULL;
- MonoClassField *f;
- while ((f = mono_class_get_fields_internal (klass, &iter))) {
- if (!f->name || strcasecmp (f->name, name) != 0)
- continue;
- guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (f->type)));
-
- if (!decode_value(f->type, val_buf, value)) {
- return FALSE;
- }
- DbgEngineErrorCode errorCode = mono_component_debugger ()->mono_de_set_interp_var (f->type, (guint8*)obj + f->offset, val_buf);
- if (errorCode != ERR_NONE) {
- return FALSE;
- }
- return TRUE;
- }
-
- iter = NULL;
- MonoProperty *p;
- MonoObject *exc;
- ERROR_DECL (error);
- while ((p = mono_class_get_properties (klass, &iter))) {
- if (!p->name || strcasecmp (p->name, name) != 0)
- continue;
- if (!p->set)
- break;
- MonoType *type = mono_method_signature_internal (p->set)->params [0];
- guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (type)));
-
- if (!decode_value(type, val_buf, value)) {
- return FALSE;
- }
- mono_runtime_try_invoke (p->set, obj, (void **)&val_buf, &exc, error);
- if (!is_ok (error) && exc == NULL)
- exc = (MonoObject*) mono_error_convert_to_exception (error);
- if (exc) {
- char *error_message = mono_string_to_utf8_checked_internal (((MonoException *)exc)->message, error);
- if (is_ok (error)) {
- PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception: %s\n", error_message);
- g_free (error_message);
- mono_error_cleanup (error);
- }
- else {
- PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception\n");
- }
- return FALSE;
- }
- return TRUE;
- }
-
- if ((klass = m_class_get_parent(klass)))
- goto handle_parent;
+ EM_ASM ({
+ MONO.mono_wasm_add_dbg_command_received (1, -1, $0, $1);
+ }, data, len);
+ mono_component_debugger ()->mono_wasm_save_thread_context();
+ mono_wasm_fire_debugger_agent_message ();
return FALSE;
}
-EMSCRIPTEN_KEEPALIVE gboolean
-mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name)
-{
- PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: v: %p klass: %p, name: %s\n", value, klass, name);
- if (!klass || !value)
- return FALSE;
-
- if (!m_class_is_valuetype (klass)) {
- PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: klass is not a valuetype. name: %s\n", mono_class_full_name (klass));
- return FALSE;
- }
-
- return invoke_getter (value, klass, name);
-}
-
-EMSCRIPTEN_KEEPALIVE void
-mono_wasm_set_is_debugger_attached (gboolean is_attached)
-{
- mono_set_is_debugger_attached (is_attached);
- if (is_attached && has_pending_lazy_loaded_assemblies)
- {
- GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies ();
- for (int i = 0; i < assemblies->len; ++i) {
- MonoAssembly *ass = (MonoAssembly*)g_ptr_array_index (assemblies, i);
- assembly_loaded (NULL, ass);
- }
- g_ptr_array_free (assemblies, TRUE);
- has_pending_lazy_loaded_assemblies = FALSE;
- }
-}
-
-// Functions required by debugger-state-machine.
-gsize
-mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData)
-{
- return 1;
-}
-
#else // HOST_WASM
void
@@ -1967,4 +448,4 @@ mono_wasm_debugger_init (void)
{
}
-#endif // HOST_WASM
\ No newline at end of file
+#endif // HOST_WASM
diff --git a/src/mono/mono/utils/mono-threads-coop.h b/src/mono/mono/utils/mono-threads-coop.h
index 47c20c140253a..975915812946a 100644
--- a/src/mono/mono/utils/mono-threads-coop.h
+++ b/src/mono/mono/utils/mono-threads-coop.h
@@ -20,7 +20,7 @@
#include "mono/metadata/icalls.h"
/* JIT specific interface */
-extern volatile size_t mono_polling_required;
+MONO_API_DATA volatile size_t mono_polling_required;
/* Internal API */
diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in
index d783615a908e1..4c50a08f007c8 100644
--- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in
+++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in
@@ -270,29 +270,29 @@
"kind": "Sdk",
"version": "${EmscriptenVersion}",
"alias-to": {
- "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.win-x64",
- "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.linux-x64",
- "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64",
- "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64"
+ "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64",
+ "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64",
+ "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64",
+ "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64"
}
},
"Microsoft.NET.Runtime.Emscripten.Python" : {
"kind": "Sdk",
"version": "${EmscriptenVersion}",
"alias-to": {
- "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.win-x64",
- "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64",
- "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64"
+ "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64",
+ "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64",
+ "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64"
}
},
"Microsoft.NET.Runtime.Emscripten.Sdk" : {
"kind": "Sdk",
"version": "${EmscriptenVersion}",
"alias-to": {
- "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.win-x64",
- "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.linux-x64",
- "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64",
- "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64"
+ "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64",
+ "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64",
+ "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64",
+ "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64"
}
}
}
diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets
index 6e885124174bd..ca7309a6612b4 100644
--- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets
+++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets
@@ -8,10 +8,9 @@
true
-
-
+
@@ -20,19 +19,28 @@
+
+
+
+
+
+
+
+
+
@@ -41,6 +49,6 @@
-
+
diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj
index 4f5a245e314de..8fb25b90b7189 100644
--- a/src/mono/sample/Android/AndroidSampleApp.csproj
+++ b/src/mono/sample/Android/AndroidSampleApp.csproj
@@ -6,7 +6,6 @@
android-$(TargetArchitecture)
true
Link
- $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)\
false
diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile
index 11f1c35df9fb4..6d59ee860fe8b 100644
--- a/src/mono/sample/Android/Makefile
+++ b/src/mono/sample/Android/Makefile
@@ -3,6 +3,7 @@ MONO_ARCH?=x64
DOTNET := ../../../../dotnet.sh
USE_LLVM=true
AOT=false
+INTERP=false
DEPLOY_AND_RUN?=true
#If DIAGNOSTIC_PORTS is enabled, RUNTIME_COMPONENTS must also be enabled.
@@ -26,6 +27,7 @@ run:
/p:Configuration=$(MONO_CONFIG) \
/p:DeployAndRun=$(DEPLOY_AND_RUN) \
/p:ForceAOT=$(AOT) \
+ /p:MonoForceInterpreter=$(INTERP) \
/p:UseLLVM=$(USE_LLVM) \
/p:RunActivity=false \
'/p:RuntimeComponents="$(RUNTIME_COMPONENTS)"' \
diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj
index e9f7c50320597..04b2bde9b0b69 100644
--- a/src/mono/sample/iOS/Program.csproj
+++ b/src/mono/sample/iOS/Program.csproj
@@ -5,7 +5,6 @@
$(NetCoreAppToolCurrent)
iOS
iOSSimulator
- $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\
$(TargetOS.ToLower())-$(TargetArchitecture)
$(DefineConstants);CI_TEST
diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj
index 090fe47dc7afa..af4ec2b324a10 100644
--- a/src/mono/sample/mbr/apple/AppleDelta.csproj
+++ b/src/mono/sample/mbr/apple/AppleDelta.csproj
@@ -3,7 +3,6 @@
Exe
bin
$(NetCoreAppToolCurrent)
- $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\
$(TargetOS.ToLower())-$(TargetArchitecture)
$(DefineConstants);CI_TEST
true
diff --git a/src/mono/sample/mbr/browser/index.html b/src/mono/sample/mbr/browser/index.html
index 472cf5f29ea05..eb19b7bb2f595 100644
--- a/src/mono/sample/mbr/browser/index.html
+++ b/src/mono/sample/mbr/browser/index.html
@@ -29,7 +29,6 @@
},
};
-
diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js
index 0856b8d0e0303..74781179a31f8 100644
--- a/src/mono/sample/mbr/browser/runtime.js
+++ b/src/mono/sample/mbr/browser/runtime.js
@@ -2,17 +2,29 @@
// The .NET Foundation licenses this file to you under the MIT license.
var Module = {
+ config: null,
+
+ preInit: async function() {
+ await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly
+ },
+
+ // Called when the runtime is initialized and wasm is ready
onRuntimeInitialized: function () {
- config.loaded_cb = function () {
+ if (!Module.config || Module.config.error) {
+ console.log("An error occured while loading the config file");
+ return;
+ }
+
+ Module.config.loaded_cb = function () {
App.init ();
};
- config.environment_variables = {
+ Module.config.environment_variables = {
"DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
};
- config.fetch_file_cb = function (asset) {
+ Module.config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
}
- MONO.mono_load_runtime_and_bcl_args (config);
+ MONO.mono_load_runtime_and_bcl_args (Module.config);
},
};
diff --git a/src/mono/sample/wasm/browser-bench/index.html b/src/mono/sample/wasm/browser-bench/index.html
index 8f7748798d45b..5c5271954f1c2 100644
--- a/src/mono/sample/wasm/browser-bench/index.html
+++ b/src/mono/sample/wasm/browser-bench/index.html
@@ -51,7 +51,6 @@
}
};
-
diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js
index a39b0b97b1490..f9bdf8b3af7ab 100644
--- a/src/mono/sample/wasm/browser-bench/runtime.js
+++ b/src/mono/sample/wasm/browser-bench/runtime.js
@@ -1,9 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
var Module = {
+ config: null,
+
+ preInit: async function() {
+ await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly
+ },
+
+ // Called when the runtime is initialized and wasm is ready
onRuntimeInitialized: function () {
- config.loaded_cb = function () {
+ if (!Module.config || Module.config.error) {
+ console.log("An error occured while loading the config file");
+ return;
+ }
+
+ Module.config.loaded_cb = function () {
try {
App.init ();
} catch (error) {
@@ -11,13 +22,21 @@ var Module = {
throw (error);
}
};
- config.fetch_file_cb = function (asset) {
+ Module.config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
}
+ if (Module.config.enable_profiler)
+ {
+ Module.config.aot_profiler_options = {
+ write_at:"Sample.Test::StopProfile",
+ send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData"
+ }
+ }
+
try
{
- MONO.mono_load_runtime_and_bcl_args (config);
+ MONO.mono_load_runtime_and_bcl_args (Module.config);
} catch (error) {
test_exit(1);
throw(error);
diff --git a/src/mono/sample/wasm/browser-profile/index.html b/src/mono/sample/wasm/browser-profile/index.html
index 50eb0ff28c2ef..de4a5599e94ad 100644
--- a/src/mono/sample/wasm/browser-profile/index.html
+++ b/src/mono/sample/wasm/browser-profile/index.html
@@ -10,67 +10,8 @@
Result from Sample.Test.TestMeaning:
-
-
-
diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js
index 1ce1c0b736034..f0636985d0358 100644
--- a/src/mono/sample/wasm/browser-profile/runtime.js
+++ b/src/mono/sample/wasm/browser-profile/runtime.js
@@ -1,22 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-var Module = {
+var Module = {
+ is_testing: false,
+ config: null,
+
+ preInit: async function() {
+ await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly
+ },
+
+ // Called when the runtime is initialized and wasm is ready
onRuntimeInitialized: function () {
- config.loaded_cb = function () {
+ if (!Module.config || Module.config.error) {
+ console.log("An error occured while loading the config file");
+ return;
+ }
+
+ Module.config.loaded_cb = function () {
try {
- App.init ();
+ Module.init();
} catch (error) {
- test_exit(1);
+ Module.test_exit(1);
throw (error);
}
};
- config.fetch_file_cb = function (asset) {
+ Module.config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
}
- if (config.enable_profiler)
+ if (Module.config.enable_profiler)
{
- config.aot_profiler_options = {
+ Module.config.aot_profiler_options = {
write_at:"Sample.Test::StopProfile",
send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData"
}
@@ -24,10 +37,63 @@ var Module = {
try
{
- MONO.mono_load_runtime_and_bcl_args (config);
+ MONO.mono_load_runtime_and_bcl_args (Module.config);
} catch (error) {
- test_exit(1);
+ Module.test_exit(1);
throw(error);
}
+ },
+
+ init: function () {
+ console.log("not ready yet")
+ var ret = BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:TestMeaning", []);
+ document.getElementById("out").innerHTML = ret;
+ console.log ("ready");
+
+ if (Module.is_testing)
+ {
+ console.debug(`ret: ${ret}`);
+ let exit_code = ret == 42 ? 0 : 1;
+ Module.test_exit(exit_code);
+ }
+
+ if (Module.config.enable_profiler) {
+ BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:StopProfile", []);
+ Module.saveProfile();
+ }
+ },
+
+ onLoad: function() {
+ var url = new URL(decodeURI(window.location));
+ let args = url.searchParams.getAll('arg');
+ Module.is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined);
+ },
+
+ test_exit: function(exit_code) {
+ if (!Module.is_testing) {
+ console.log(`test_exit: ${exit_code}`);
+ return;
+ }
+
+ /* Set result in a tests_done element, to be read by xharness */
+ var tests_done_elem = document.createElement("label");
+ tests_done_elem.id = "tests_done";
+ tests_done_elem.innerHTML = exit_code.toString();
+ document.body.appendChild(tests_done_elem);
+
+ console.log(`WASM EXIT ${exit_code}`);
+ },
+
+ saveProfile: function () {
+ var a = document.createElement('a');
+ var blob = new Blob([Module.aot_profile_data]);
+ a.href = URL.createObjectURL(blob);
+ a.download = "data.aotprofile";
+ // Append anchor to body.
+ document.body.appendChild(a);
+ a.click();
+
+ // Remove anchor from body
+ document.body.removeChild(a);
}
-};
\ No newline at end of file
+};
diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj
index 2b05836627673..15386dece90a9 100644
--- a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj
+++ b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj
@@ -1,7 +1,11 @@
+ Debug
true
runtime.js
+ true
+ embedded
+ 1
diff --git a/src/mono/sample/wasm/browser/index.html b/src/mono/sample/wasm/browser/index.html
index bd8e5015a0ee5..5f170bb38e7bc 100644
--- a/src/mono/sample/wasm/browser/index.html
+++ b/src/mono/sample/wasm/browser/index.html
@@ -48,7 +48,6 @@
},
};
-
diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js
index a39b0b97b1490..816136acf36b6 100644
--- a/src/mono/sample/wasm/browser/runtime.js
+++ b/src/mono/sample/wasm/browser/runtime.js
@@ -2,8 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.
var Module = {
+
+ config: null,
+
+ preInit: async function() {
+ await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly
+ },
+
+ // Called when the runtime is initialized and wasm is ready
onRuntimeInitialized: function () {
- config.loaded_cb = function () {
+ if (!Module.config || Module.config.error) {
+ console.log("No config found");
+ return;
+ }
+
+ Module.config.loaded_cb = function () {
try {
App.init ();
} catch (error) {
@@ -11,13 +24,13 @@ var Module = {
throw (error);
}
};
- config.fetch_file_cb = function (asset) {
+ Module.config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
}
try
{
- MONO.mono_load_runtime_and_bcl_args (config);
+ MONO.mono_load_runtime_and_bcl_args (Module.config);
} catch (error) {
test_exit(1);
throw(error);
diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile
index 9f6d055511193..86288cd674e70 100644
--- a/src/mono/wasm/Makefile
+++ b/src/mono/wasm/Makefile
@@ -48,12 +48,20 @@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION)
@echo "----------------------------------------------------------"
@echo "Installed emsdk into EMSDK_PATH=$(TOP)/src/mono/wasm/emsdk"
+# FIXME: When https://github.com/dotnet/runtime/issues/54565 is fixed, and the WasmApp targets are updated to use mono runtime components, remove this
+MONO_COMPONENT_LIBS= \
+ $(MONO_BIN_DIR)/libmono-component-hot_reload-static.a \
+ $(MONO_BIN_DIR)/libmono-component-diagnostics_tracing-stub-static.a
+
MONO_OBJ_DIR=$(OBJDIR)/mono/Browser.wasm.$(CONFIG)
MONO_INCLUDE_DIR=$(MONO_BIN_DIR)/include/mono-2.0
BUILDS_OBJ_DIR=$(MONO_OBJ_DIR)/wasm
+# libmonosgen-2.0 is in MONO_LIBS twice because the components and the runtime are depend on each other
MONO_LIBS = \
$(MONO_BIN_DIR)/libmono-ee-interp.a \
$(MONO_BIN_DIR)/libmonosgen-2.0.a \
+ $(MONO_COMPONENT_LIBS) \
+ $(MONO_BIN_DIR)/libmonosgen-2.0.a \
$(MONO_BIN_DIR)/libmono-ilgen.a \
$(MONO_BIN_DIR)/libmono-icall-table.a \
$(MONO_BIN_DIR)/libmono-profiler-aot.a \
diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md
index a44edd53dc3dd..67cf64b8c0576 100644
--- a/src/mono/wasm/README.md
+++ b/src/mono/wasm/README.md
@@ -13,7 +13,7 @@ Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building ta
you are directly using the `dotnet build`, or `build.sh`.
* Alternatively you can install **correct version** yourself from the [Emscripten SDK guide](https://emscripten.org/docs/getting_started/downloads.html).
-Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.21`. See [emscripten-version.txt](./emscripten-version.txt)
+Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.23`. See [emscripten-version.txt](./emscripten-version.txt)
Make sure to set `EMSDK_PATH` variable, whenever building, or running tests for wasm.
@@ -154,3 +154,14 @@ The samples in `src/mono/sample/wasm` can be build and run like this:
`dotnet build /t:RunSample browser/Wasm.Browser.Sample.csproj`
To build and run the samples with AOT, add `/p:RunAOTCompilation=true` to the above command lines.
+
+### Upgrading Emscripten
+
+Bumping Emscripten version involves these steps:
+
+* update https://github.com/dotnet/runtime/blob/main/src/mono/wasm/emscripten-version.txt
+* bump emscripten versions in docker images in https://github.com/dotnet/dotnet-buildtools-prereqs-docker
+* bump emscripten in https://github.com/dotnet/emsdk
+* update version number in docs
+* update `Microsoft.NET.Runtime.Emscripten..Node.win-x64` package name, version and sha hash in https://github.com/dotnet/runtime/blob/main/eng/Version.Details.xml and in https://github.com/dotnet/runtime/blob/main/eng/Versions.props. the sha is the commit hash in https://github.com/dotnet/emsdk and the package version can be found at https://dev.azure.com/dnceng/public/_packaging?_a=feed&feed=dotnet6
+* update packages in the workload manifest https://github.com/dotnet/runtime/blob/main/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in
diff --git a/src/mono/wasm/build/WasmApp.InTree.props b/src/mono/wasm/build/WasmApp.InTree.props
index 42b7574251468..e5968f1320a94 100644
--- a/src/mono/wasm/build/WasmApp.InTree.props
+++ b/src/mono/wasm/build/WasmApp.InTree.props
@@ -6,7 +6,6 @@
AnyCPU
false
$(NetCoreAppToolCurrent)
- $([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(Configuration), 'runtimes', 'browser-wasm'))
$([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk'))
false
true
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index b16195a618af1..e1de26e4a4cde 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -3,6 +3,7 @@
+
<_WasmBuildNativeCoreDependsOn>
@@ -165,7 +166,7 @@
<_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz
$(_EmccOptimizationFlagDefault)
- $(_EmccOptimizationFlagDefault)
+ -O0
@@ -173,10 +174,6 @@
<_EmccCommonFlags Include="$(EmccFlags)" />
<_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" />
<_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" />
- <_EmccCommonFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
- <_EmccCommonFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
- <_EmccCommonFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
- <_EmccCommonFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" />
@@ -186,11 +183,7 @@
- <_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" />
- <_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" />
-
<_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" />
- <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" />
@@ -227,54 +220,84 @@
<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
<_EmccCFlags Include="@(_EmccCommonFlags)" />
+
+ <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
+ <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
+ <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
+ <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DCORE_BINDINGS" />
<_EmccCFlags Include="-DGEN_PINVOKE=1" />
+
<_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" />
<_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" />
<_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'"%(Identity)"', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" />
<_EmccCFlags Include="$(EmccExtraCFlags)" />
+
+ <_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" />
+ <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" />
+ <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
+ <_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" />
+ <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat
+ <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py
<_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp
+
+
+
-
+
- <_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" />
- <_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" />
- <_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" />
-
<_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" />
<_EmccLDFlags Include="@(_EmccCommonFlags)" />
+
<_EmccLDFlags Include="-s TOTAL_MEMORY=536870912" />
<_EmccLDFlags Include="$(EmccExtraLDFlags)" />
+
+
+
+
+
+
+ <_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />
+ <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />
+
+
+ <_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" />
- <_EmccLDFlags Include="--js-library "%(_DotnetJSSrcFile.Identity)"" />
- <_EmccLDFlags Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />
+ <_EmccLinkStepArgs Include="@(_EmccLDFlags)" />
+ <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" />
+ <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />
- <_EmccLDFlags Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />
- <_EmccLDFlags Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" />
+ <_EmccLinkStepArgs Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />
+ <_EmccLinkStepArgs Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" />
- <_EmccLDFlags Include=""%(_WasmNativeFileForLinking.Identity)"" />
- <_EmccLDFlags Include=""%(_WasmObjects.Identity)"" />
- <_EmccLDFlags Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" />
+ <_EmccLinkStepArgs Include=""%(_WasmNativeFileForLinking.Identity)"" />
+ <_EmccLinkStepArgs Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" />
<_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp
-
-
+
+
+
+
@@ -289,7 +312,7 @@
- $(EmccFlags) -DDRIVER_GEN=1
+ $(EmccExtraCFlags) -DDRIVER_GEN=1
void mono_profiler_init_aot (const char *desc)%3B
EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_profiler_init_aot (desc)%3B }
@@ -336,6 +359,10 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
-->
+
+ <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))
+
+
@@ -353,6 +380,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
@(MonoAOTCompilerDefaultAotArguments, ';')
@(MonoAOTCompilerDefaultProcessArguments, ';')
+
<_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
@@ -404,7 +432,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
Profilers="$(WasmProfilers)"
AotModulesTablePath="$(_WasmIntermediateOutputPath)driver-gen.c"
UseLLVM="true"
- DisableParallelAot="true"
+ DisableParallelAot="$(DisableParallelAot)"
DedupAssembly="$(_WasmDedupAssembly)"
LLVMDebug="dwarfdebug"
LLVMPath="$(EmscriptenUpstreamBinPath)" >
@@ -419,12 +447,8 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" />
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
-
- <_WasmNativeFileForLinking Include="@(_BitcodeFile)" />
+ <_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
-
-
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index 2141cce41b69b..034287436892b 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -61,11 +61,10 @@
Public items:
- @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir).
(relative path can be set via %(TargetPath) metadata)
- - @(WasmSatelliteAssemblies)
- @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs
- @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle.
- - @(WasmExtraConfig) - json elements to add to `mono-config.js`
+ - @(WasmExtraConfig) - json elements to add to `mono-config.json`
Eg.
- Value attribute can have a number, bool, quoted string, or json string
@@ -115,6 +114,13 @@
<_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
+
+ <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" />
+ <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" />
+
+ <_WasmSatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" />
+
+ <_WasmAssembliesInternal Remove="@(_WasmSatelliteAssemblies)" />
@@ -142,7 +148,7 @@
MainJS="$(WasmMainJSPath)"
Assemblies="@(_WasmAssembliesInternal)"
InvariantGlobalization="$(InvariantGlobalization)"
- SatelliteAssemblies="@(WasmSatelliteAssemblies)"
+ SatelliteAssemblies="@(_WasmSatelliteAssemblies)"
FilesToIncludeInFileSystem="@(WasmFilesToIncludeInFileSystem)"
IcuDataFileName="$(WasmIcuDataFileName)"
RemoteSources="@(WasmRemoteSources)"
@@ -193,11 +199,5 @@
-
-
-
diff --git a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
similarity index 75%
rename from src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj
rename to src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
index f7b9d3fcc4fd0..cdd5e95a4e210 100644
--- a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj
+++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
@@ -13,13 +13,18 @@
true
false
PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)
+
+ <_PropsFile>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).props
-
+
+
+
$(TestRootDir)AppBundle\
$(OriginalPublishDir)WasmTestRunner.dll
@@ -28,17 +33,15 @@
-
-
-
-
+
<_ExtraFiles Include="$(ExtraFilesPath)**\*" />
- <_SatelliteAssembliesForVFS Include="@(WasmSatelliteAssemblies)" />
+ <_SatelliteAssembliesForVFS Include="$(OriginalPublishDir)**\*.resources.dll" />
+ <_SatelliteAssembliesForVFS CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" />
<_SatelliteAssembliesForVFS TargetPath="%(CultureName)\%(FileName)%(Extension)" />
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
index 1f31e044653ab..81c419e4c5850 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
@@ -317,6 +317,7 @@ internal class MethodInfo
public SourceId SourceId => source.SourceId;
+ public int DebuggerId { get; set; }
public string Name { get; }
public MethodDebugInformation DebugInformation;
public MethodDefinitionHandle methodDefHandle;
@@ -325,7 +326,7 @@ internal class MethodInfo
public SourceLocation EndLocation { get; }
public AssemblyInfo Assembly { get; }
public int Token { get; }
-
+ public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0;
public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type)
{
this.Assembly = assembly;
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
index efb2a7b4a2a79..f39f2736caaeb 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
@@ -172,50 +172,33 @@ internal class MonoCommands
public MonoCommands(string expression) => this.expression = expression;
- public static MonoCommands GetCallStack() => new MonoCommands("MONO.mono_wasm_get_call_stack()");
-
public static MonoCommands GetExceptionObject() => new MonoCommands("MONO.mono_wasm_get_exception_object()");
- public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready");
+ public static MonoCommands GetDebuggerAgentBufferReceived() => new MonoCommands("MONO.mono_wasm_get_dbg_command_info()");
- public static MonoCommands StartSingleStepping(StepKind kind) => new MonoCommands($"MONO.mono_wasm_start_single_stepping ({(int)kind})");
+ public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready");
public static MonoCommands GetLoadedFiles() => new MonoCommands("MONO.mono_wasm_get_loaded_files()");
- public static MonoCommands ClearAllBreakpoints() => new MonoCommands("MONO.mono_wasm_clear_all_breakpoints()");
-
- public static MonoCommands GetDetails(DotnetObjectId objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ('{objectId}', {(args ?? "{ }")})");
-
- public static MonoCommands GetScopeVariables(int scopeId, params VarInfo[] vars)
- {
- var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray();
- return new MonoCommands($"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(var_ids)})");
- }
-
- public static MonoCommands SetVariableValue(int scopeId, int index, string name, string newValue)
+ public static MonoCommands SendDebuggerAgentCommand(int id, int command_set, int command, string command_parameters)
{
- return new MonoCommands($"MONO.mono_wasm_set_variable_value({scopeId}, {index}, '{name}', '{newValue}')");
+ return new MonoCommands($"MONO.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')");
}
- public static MonoCommands EvaluateMemberAccess(int scopeId, string expr, params VarInfo[] vars)
+ public static MonoCommands SendDebuggerAgentCommandWithParms(int id, int command_set, int command, string command_parameters, int len, int type, string parm)
{
- var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray();
- return new MonoCommands($"MONO.mono_wasm_eval_member_access({scopeId}, {JsonConvert.SerializeObject(var_ids)}, '', '{expr}')");
+ return new MonoCommands($"MONO.mono_wasm_send_dbg_command_with_parms ({id}, {command_set}, {command},'{command_parameters}', {len}, {type}, '{parm}')");
}
- public static MonoCommands SetBreakpoint(string assemblyName, int methodToken, int ilOffset) => new MonoCommands($"MONO.mono_wasm_set_breakpoint (\"{assemblyName}\", {methodToken}, {ilOffset})");
-
- public static MonoCommands RemoveBreakpoint(int breakpointId) => new MonoCommands($"MONO.mono_wasm_remove_breakpoint({breakpointId})");
-
- public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')");
-
public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString()})");
- public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()");
+ public static MonoCommands GetDetails(int objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ({objectId}, {(args ?? "{ }")})");
- public static MonoCommands SetPauseOnExceptions(string state) => new MonoCommands($"MONO.mono_wasm_set_pause_on_exceptions(\"{state}\")");
+ public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()");
public static MonoCommands DetachDebugger() => new MonoCommands($"MONO.mono_wasm_detach_debugger()");
+
+ public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')");
}
internal enum MonoErrorCodes
@@ -280,8 +263,8 @@ internal enum BreakpointState
internal enum StepKind
{
Into,
- Out,
- Over
+ Over,
+ Out
}
internal class ExecutionContext
@@ -292,6 +275,7 @@ internal class ExecutionContext
public TaskCompletionSource ready;
public bool IsRuntimeReady => ready != null && ready.Task.IsCompleted;
+ public int ThreadId { get; set; }
public int Id { get; set; }
public object AuxData { get; set; }
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs
index 21f8dabdb37a6..c4e578a0a2c92 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs
@@ -8,6 +8,7 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using System.IO;
namespace Microsoft.WebAssembly.Diagnostics
{
@@ -18,7 +19,6 @@ internal class MemberReferenceResolver
private MonoProxy proxy;
private ExecutionContext ctx;
private PerScopeCache scopeCache;
- private VarInfo[] varIds;
private ILogger logger;
private bool locals_fetched;
@@ -31,44 +31,99 @@ public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId
this.logger = logger;
scopeCache = ctx.GetCacheForScope(scope_id);
}
-
- // Checks Locals, followed by `this`
- public async Task Resolve(string var_name, CancellationToken token)
+ public async Task GetValueFromObject(JToken objRet, CancellationToken token)
{
- if (scopeCache.Locals.Count == 0 && !locals_fetched)
+ if (objRet["value"]?["className"]?.Value() == "System.Exception")
{
- Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token);
- if (scope_res.IsErr)
- throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}");
- locals_fetched = true;
+ if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId))
+ {
+ var exceptionObject = await proxy.sdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token);
+ var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("_message"));
+ exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value() + ": " + exceptionObjectMessage["value"]?["value"]?.Value();
+ return exceptionObjectMessage["value"]?.Value();
+ }
+ return objRet["value"]?.Value();
}
- if (scopeCache.Locals.TryGetValue(var_name, out JObject obj))
+ if (objRet["value"]?.Value() != null)
+ return objRet["value"]?.Value();
+ if (objRet["get"]?.Value() != null)
{
- return obj["value"]?.Value();
- }
+ if (DotnetObjectId.TryParse(objRet?["get"]?["objectIdValue"]?.Value(), out DotnetObjectId objectId))
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.WriteObj(objectId, proxy.sdbHelper);
+ var ret = await proxy.sdbHelper.InvokeMethod(sessionId, command_params.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token);
+ return await GetValueFromObject(ret, token);
+ }
- if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret))
- return ret;
-
- if (varIds == null)
- {
- Frame scope = ctx.CallStack.FirstOrDefault(s => s.Id == scopeId);
- varIds = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);
}
+ return null;
+ }
+ // Checks Locals, followed by `this`
+ public async Task Resolve(string var_name, CancellationToken token)
+ {
+ string[] parts = var_name.Split(".");
+ JObject rootObject = null;
- Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.EvaluateMemberAccess(scopeId, var_name, varIds), token);
- if (res.IsOk)
- {
- ret = res.Value?["result"]?["value"]?["value"]?.Value();
- scopeCache.MemberReferences[var_name] = ret;
+ if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) {
+ return ret;
}
- else
+ foreach (string part in parts)
{
- logger.LogDebug(res.Error.ToString());
- }
+ string partTrimmed = part.Trim();
+ if (partTrimmed == "")
+ return null;
+ if (rootObject != null)
+ {
+ if (rootObject?["subtype"]?.Value() == "null")
+ return null;
+ if (DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId))
+ {
+ var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token);
+ var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed);
+ if (objRet == null)
+ return null;
- return ret;
+ rootObject = await GetValueFromObject(objRet, token);
+ }
+ continue;
+ }
+ if (scopeCache.Locals.Count == 0 && !locals_fetched)
+ {
+ Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token);
+ if (scope_res.IsErr)
+ throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}");
+ locals_fetched = true;
+ }
+ if (scopeCache.Locals.TryGetValue(partTrimmed, out JObject obj))
+ {
+ rootObject = obj["value"]?.Value();
+ }
+ else if (scopeCache.Locals.TryGetValue("this", out JObject objThis))
+ {
+ if (partTrimmed == "this")
+ {
+ rootObject = objThis?["value"].Value();
+ }
+ else if (DotnetObjectId.TryParse(objThis?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId))
+ {
+ var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token);
+ var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed);
+ if (objRet != null)
+ {
+ rootObject = await GetValueFromObject(objRet, token);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+ }
+ scopeCache.MemberReferences[var_name] = rootObject;
+ return rootObject;
}
}
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
index b80a4483ca02d..689904740d3d6 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
@@ -18,6 +18,7 @@ namespace Microsoft.WebAssembly.Diagnostics
{
internal class MonoProxy : DevToolsProxy
{
+ internal MonoSDBHelper sdbHelper;
private IList urlSymbolServerList;
private static HttpClient client = new HttpClient();
private HashSet sessions = new HashSet();
@@ -26,6 +27,7 @@ internal class MonoProxy : DevToolsProxy
public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory)
{
this.urlSymbolServerList = urlSymbolServerList ?? new List();
+ sdbHelper = new MonoSDBHelper(this);
}
internal ExecutionContext GetContext(SessionId sessionId)
@@ -132,11 +134,15 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth
await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
return true;
}
- case "mono_wasm_fire_bp":
- case "_mono_wasm_fire_bp":
- case "_mono_wasm_fire_exception":
+ case "_mono_wasm_fire_debugger_agent_message":
{
- return await OnPause(sessionId, args, token);
+ try {
+ return await OnReceiveDebuggerAgentEvent(sessionId, args, token);
+ }
+ catch (Exception) //if the page is refreshed maybe it stops here.
+ {
+ return false;
+ }
}
}
break;
@@ -370,8 +376,12 @@ protected override async Task AcceptCommand(MessageId id, string method, J
if (!DotnetObjectId.TryParse(args?["objectId"], out DotnetObjectId objectId))
break;
- Result result = await RuntimeGetProperties(id, objectId, args, token);
- SendResponse(id, result, token);
+ var ret = await RuntimeGetPropertiesInternal(id, objectId, args, token);
+ if (ret == null) {
+ SendResponse(id, Result.Err($"Unable to RuntimeGetProperties '{objectId}'"), token);
+ }
+ else
+ SendResponse(id, Result.OkFromObject(new { result = ret }), token);
return true;
}
@@ -388,7 +398,7 @@ protected override async Task AcceptCommand(MessageId id, string method, J
case "Debugger.setPauseOnExceptions":
{
string state = args["state"].Value();
- await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions(state), token);
+ await sdbHelper.EnableExceptions(id, state, token);
// Pass this on to JS too
return false;
}
@@ -403,8 +413,6 @@ protected override async Task AcceptCommand(MessageId id, string method, J
}
case "DotnetDebugger.getMethodLocation":
{
- Console.WriteLine("set-breakpoint-by-method: " + id + " " + args);
-
DebugStore store = await RuntimeReady(id, token);
string aname = args["assemblyName"]?.Value();
string typeName = args["typeName"]?.Value();
@@ -460,31 +468,80 @@ protected override async Task AcceptCommand(MessageId id, string method, J
}
case "Runtime.callFunctionOn":
{
- if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId))
- return false;
-
- if (objectId.Scheme == "scope")
- {
+ try {
+ return await CallOnFunction(id, args, token);
+ }
+ catch (Exception){
SendResponse(id,
Result.Exception(new ArgumentException(
- $"Runtime.callFunctionOn not supported with scope ({objectId}).")),
+ $"Runtime.callFunctionOn not supported with ({args["objectId"]}).")),
token);
return true;
}
-
- Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token);
- JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;
-
- if (res.IsOk && res_value_type == JTokenType.Object || res_value_type == JTokenType.Object)
- res = Result.OkFromObject(new { result = res.Value["result"]["value"] });
-
- SendResponse(id, res, token);
- return true;
}
}
return false;
}
+ private async Task CallOnFunction(MessageId id, JObject args, CancellationToken token)
+ {
+ if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) {
+ return false;
+ }
+ switch (objectId.Scheme)
+ {
+ case "object":
+ args["details"] = await sdbHelper.GetObjectProxy(id, int.Parse(objectId.Value), token);
+ break;
+ case "valuetype":
+ args["details"] = await sdbHelper.GetValueTypeProxy(id, int.Parse(objectId.Value), token);
+ break;
+ case "pointer":
+ args["details"] = await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token);
+ break;
+ case "array":
+ args["details"] = await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token);
+ break;
+ case "cfo_res":
+ {
+ Result cfo_res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token);
+ cfo_res = Result.OkFromObject(new { result = cfo_res.Value?["result"]?["value"]});
+ SendResponse(id, cfo_res, token);
+ return true;
+ }
+ case "scope":
+ {
+ SendResponse(id,
+ Result.Exception(new ArgumentException(
+ $"Runtime.callFunctionOn not supported with scope ({objectId}).")),
+ token);
+ return true;
+ }
+ default:
+ return false;
+ }
+ Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token);
+ if (res.IsErr)
+ {
+ SendResponse(id, res, token);
+ return true;
+ }
+ if (res.Value?["result"]?["value"]?["type"] == null) //it means that is not a buffer returned from the debugger-agent
+ {
+ byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value());
+ var ret_debugger_cmd = new MemoryStream(newBytes);
+ var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd);
+ ret_debugger_cmd_reader.ReadByte(); //number of objects returned.
+ var obj = await sdbHelper.CreateJObjectForVariableValue(id, ret_debugger_cmd_reader, "ret", false, -1, token);
+ /*JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;*/
+ res = Result.OkFromObject(new { result = obj["value"]});
+ SendResponse(id, res, token);
+ return true;
+ }
+ res = Result.OkFromObject(new { result = res.Value?["result"]?["value"]});
+ SendResponse(id, res, token);
+ return true;
+ }
private async Task OnSetVariableValue(MessageId id, int scopeId, string varName, JToken varValue, CancellationToken token)
{
@@ -498,50 +555,59 @@ private async Task OnSetVariableValue(MessageId id, int scopeId, string va
var varToSetValue = varIds.FirstOrDefault(v => v.Name == varName);
if (varToSetValue == null)
return false;
- Result res = await SendMonoCommand(id, MonoCommands.SetVariableValue(scopeId, varToSetValue.Index, varName, varValue["value"].Value()), token);
- if (res.IsOk)
+ var res = await sdbHelper.SetVariableValue(id, ctx.ThreadId, scopeId, varToSetValue.Index, varValue["value"].Value(), token);
+ if (res)
SendResponse(id, Result.Ok(new JObject()), token);
else
SendResponse(id, Result.Err($"Unable to set '{varValue["value"].Value()}' to variable '{varName}'"), token);
return true;
}
- private async Task RuntimeGetProperties(MessageId id, DotnetObjectId objectId, JToken args, CancellationToken token)
+ internal async Task RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token)
{
- if (objectId.Scheme == "scope")
+ var accessorPropertiesOnly = false;
+ var ownProperties = false;
+ if (args != null)
{
- return await GetScopeProperties(id, int.Parse(objectId.Value), token);
+ if (args["accessorPropertiesOnly"] != null)
+ accessorPropertiesOnly = args["accessorPropertiesOnly"].Value();
+ if (args["ownProperties"] != null)
+ ownProperties = args["ownProperties"].Value();
}
-
- Result res = await SendMonoCommand(id, MonoCommands.GetDetails(objectId, args), token);
- if (res.IsErr)
- return res;
-
- if (objectId.Scheme == "cfo_res")
- {
- // Runtime.callFunctionOn result object
- string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value();
- if (value_json_str != null)
+ //Console.WriteLine($"RuntimeGetProperties - {args}");
+ try {
+ switch (objectId.Scheme)
{
- res = Result.OkFromObject(new
+ case "scope":
{
- result = JArray.Parse(value_json_str)
- });
- }
- else
- {
- res = Result.OkFromObject(new { result = new { } });
+ var res = await GetScopeProperties(id, int.Parse(objectId.Value), token);
+ return res.Value?["result"];
+ }
+ case "valuetype":
+ return await sdbHelper.GetValueTypeValues(id, int.Parse(objectId.Value), accessorPropertiesOnly, token);
+ case "array":
+ return await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token);
+ case "object":
+ return await sdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token);
+ case "pointer":
+ return new JArray{await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)};
+ case "cfo_res":
+ {
+ Result res = await SendMonoCommand(id, MonoCommands.GetDetails(int.Parse(objectId.Value), args), token);
+ string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value();
+ return value_json_str != null ? JArray.Parse(value_json_str) : null;
+ }
+ default:
+ return null;
+
}
}
- else
- {
- res = Result.Ok(JObject.FromObject(new { result = res.Value["result"]["value"] }));
+ catch (Exception) {
+ return null;
}
-
- return res;
}
- private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, JObject mono_frame, Breakpoint bp, CancellationToken token)
+ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, Frame mono_frame, Breakpoint bp, CancellationToken token)
{
if (string.IsNullOrEmpty(bp?.Condition) || mono_frame == null)
return true;
@@ -551,8 +617,7 @@ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext
if (bp.ConditionAlreadyEvaluatedWithError)
return false;
try {
- var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame["frame_id"].Value(), logger);
-
+ var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame.Id, logger);
JObject retValue = await resolver.Resolve(condition, token);
if (retValue == null)
retValue = await EvaluateExpression.CompileAndRunTheExpression(condition, resolver, token);
@@ -573,172 +638,124 @@ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext
}
return false;
}
-
- private async Task OnPause(SessionId sessionId, JObject args, CancellationToken token)
+ private async Task SendCallStack(SessionId sessionId, ExecutionContext context, string reason, int thread_id, Breakpoint bp, JObject data, IEnumerable orig_callframes, CancellationToken token)
{
- //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
- Result res = await SendMonoCommand(sessionId, MonoCommands.GetCallStack(), token);
- IEnumerable orig_callframes = args?["callFrames"]?.Values();
- ExecutionContext context = GetContext(sessionId);
- JObject data = null;
- string reason = "other";//other means breakpoint
-
- if (res.IsErr)
- {
- //Give up and send the original call stack
- return false;
- }
-
- //step one, figure out where did we hit
- JToken res_value = res.Value?["result"]?["value"];
- if (res_value == null || res_value is JValue)
- {
- //Give up and send the original call stack
- return false;
- }
-
- Log("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}");
- int? bp_id = res_value?["breakpoint_id"]?.Value();
- Log("verbose", $"We just hit bp {bp_id}");
- if (!bp_id.HasValue)
- {
- //Give up and send the original call stack
- return false;
- }
-
- Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == bp_id.Value);
-
var callFrames = new List();
- foreach (JObject frame in orig_callframes)
- {
- string function_name = frame["functionName"]?.Value();
- string url = frame["url"]?.Value();
- if ("mono_wasm_fire_bp" == function_name || "_mono_wasm_fire_bp" == function_name ||
- "_mono_wasm_fire_exception" == function_name)
+ var frames = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(thread_id);
+ command_params_writer.Write(0);
+ command_params_writer.Write(-1);
+ var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdThread.GetFrameInfo, command_params, token);
+ var frame_count = ret_debugger_cmd_reader.ReadInt32();
+ //Console.WriteLine("frame_count - " + frame_count);
+ for (int j = 0; j < frame_count; j++) {
+ var frame_id = ret_debugger_cmd_reader.ReadInt32();
+ var method_id = ret_debugger_cmd_reader.ReadInt32();
+ var il_pos = ret_debugger_cmd_reader.ReadInt32();
+ var flags = ret_debugger_cmd_reader.ReadByte();
+ var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token);
+ var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token);
+ var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token);
+ var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token);
+ DebugStore store = await LoadStore(sessionId, token);
+ AssemblyInfo asm = store.GetAssemblyByName(assembly_name);
+ if (asm == null)
{
- if ("_mono_wasm_fire_exception" == function_name)
+ assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly
+ asm = store.GetAssemblyByName(assembly_name);
+ if (asm == null)
{
- Result exception_obj_id = await SendMonoCommand(sessionId, MonoCommands.GetExceptionObject(), token);
- JToken res_val = exception_obj_id.Value?["result"]?["value"];
- var exception_dotnet_obj_id = new DotnetObjectId("object", res_val?["exception_id"]?.Value());
- data = JObject.FromObject(new
- {
- type = "object",
- subtype = "error",
- className = res_val?["class_name"]?.Value(),
- uncaught = res_val?["uncaught"]?.Value(),
- description = res_val?["message"]?.Value() + "\n",
- objectId = exception_dotnet_obj_id.ToString()
- });
- reason = "exception";
+ Log("debug", $"Unable to find assembly: {assembly_name}");
+ continue;
}
+ }
- var frames = new List();
- IEnumerable the_mono_frames = res.Value?["result"]?["value"]?["frames"]?.Values();
+ MethodInfo method = asm.GetMethodByToken(method_token);
- foreach (JObject mono_frame in the_mono_frames)
+ if (method == null && !asm.HasSymbols)
+ {
+ try
{
- int frame_id = mono_frame["frame_id"].Value();
- int il_pos = mono_frame["il_pos"].Value();
- int method_token = mono_frame["method_token"].Value();
- string assembly_name = mono_frame["assembly_name"].Value();
+ method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token);
+ }
+ catch (Exception e)
+ {
+ Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}");
+ continue;
+ }
+ }
- // This can be different than `method.Name`, like in case of generic methods
- string method_name = mono_frame["method_name"]?.Value();
+ if (method == null)
+ {
+ Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
+ continue;
+ }
- DebugStore store = await LoadStore(sessionId, token);
- AssemblyInfo asm = store.GetAssemblyByName(assembly_name);
- if (asm == null)
- {
- Log("debug", $"Unable to find assembly: {assembly_name}");
- continue;
- }
+ method.DebuggerId = method_id;
- MethodInfo method = asm.GetMethodByToken(method_token);
+ SourceLocation location = method?.GetLocationByIl(il_pos);
- if (method == null && !asm.HasSymbols)
- {
- try
- {
- method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token);
- }
- catch (Exception e)
- {
- Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}");
- continue;
- }
- }
+ // When hitting a breakpoint on the "IncrementCount" method in the standard
+ // Blazor project template, one of the stack frames is inside mscorlib.dll
+ // and we get location==null for it. It will trigger a NullReferenceException
+ // if we don't skip over that stack frame.
+ if (location == null)
+ {
+ continue;
+ }
- if (method == null)
- {
- Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
- continue;
- }
+ Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
+ Log("debug", $"\tmethod {method_name} location: {location}");
+ frames.Add(new Frame(method, location, frame_id));
- SourceLocation location = method?.GetLocationByIl(il_pos);
+ callFrames.Add(new
+ {
+ functionName = method_name,
+ callFrameId = $"dotnet:scope:{frame_id}",
+ functionLocation = method.StartLocation.AsLocation(),
- // When hitting a breakpoint on the "IncrementCount" method in the standard
- // Blazor project template, one of the stack frames is inside mscorlib.dll
- // and we get location==null for it. It will trigger a NullReferenceException
- // if we don't skip over that stack frame.
- if (location == null)
- {
- continue;
- }
+ location = location.AsLocation(),
- Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
- Log("debug", $"\tmethod {method_name} location: {location}");
- frames.Add(new Frame(method, location, frame_id));
+ url = store.ToUrl(location),
- callFrames.Add(new
+ scopeChain = new[]
{
- functionName = method_name,
- callFrameId = $"dotnet:scope:{frame_id}",
- functionLocation = method.StartLocation.AsLocation(),
-
- location = location.AsLocation(),
-
- url = store.ToUrl(location),
-
- scopeChain = new[]
- {
- new
+ new
+ {
+ type = "local",
+ @object = new
{
- type = "local",
- @object = new
- {
- @type = "object",
- className = "Object",
- description = "Object",
- objectId = $"dotnet:scope:{frame_id}",
- },
- name = method_name,
- startLocation = method.StartLocation.AsLocation(),
- endLocation = method.EndLocation.AsLocation(),
- }
- }
- });
-
- context.CallStack = frames;
+ @type = "object",
+ className = "Object",
+ description = "Object",
+ objectId = $"dotnet:scope:{frame_id}",
+ },
+ name = method_name,
+ startLocation = method.StartLocation.AsLocation(),
+ endLocation = method.EndLocation.AsLocation(),
+ }
+ }
+ });
- }
- if (!await EvaluateCondition(sessionId, context, the_mono_frames?.First(), bp, token))
- {
- await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
- return true;
- }
- }
- else if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) ||
- url.StartsWith("wasm://wasm/", StringComparison.Ordinal)))
- {
- callFrames.Add(frame);
- }
+ context.CallStack = frames;
+ context.ThreadId = thread_id;
}
-
string[] bp_list = new string[bp == null ? 0 : 1];
if (bp != null)
bp_list[0] = bp.StackId;
+ foreach (JObject frame in orig_callframes)
+ {
+ string function_name = frame["functionName"]?.Value();
+ string url = frame["url"]?.Value();
+ if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) ||
+ url.StartsWith("wasm://wasm/", StringComparison.Ordinal) || function_name == "_mono_wasm_fire_debugger_agent_message"))
+ {
+ callFrames.Add(frame);
+ }
+ }
var o = JObject.FromObject(new
{
callFrames,
@@ -746,10 +763,72 @@ private async Task OnPause(SessionId sessionId, JObject args, Cancellation
data,
hitBreakpoints = bp_list,
});
-
+ if (!await EvaluateCondition(sessionId, context, context.CallStack.First(), bp, token))
+ {
+ await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
+ return true;
+ }
SendEvent(sessionId, "Debugger.paused", o, token);
+
return true;
}
+ private async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObject args, CancellationToken token)
+ {
+ Result res = await SendMonoCommand(sessionId, MonoCommands.GetDebuggerAgentBufferReceived(), token);
+ if (res.IsErr)
+ return false;
+
+ ExecutionContext context = GetContext(sessionId);
+ byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value());
+ var ret_debugger_cmd = new MemoryStream(newBytes);
+ var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd);
+ ret_debugger_cmd_reader.ReadBytes(11); //skip HEADER_LEN
+ ret_debugger_cmd_reader.ReadByte(); //suspend_policy
+ var number_of_events = ret_debugger_cmd_reader.ReadInt32(); //number of events -> should be always one
+ for (int i = 0 ; i < number_of_events; i++) {
+ var event_kind = (EventKind)ret_debugger_cmd_reader.ReadByte(); //event kind
+ var request_id = ret_debugger_cmd_reader.ReadInt32(); //request id
+ if (event_kind == EventKind.Step)
+ await sdbHelper.ClearSingleStep(sessionId, request_id, token);
+ int thread_id = ret_debugger_cmd_reader.ReadInt32();
+ switch (event_kind)
+ {
+ case EventKind.Exception:
+ {
+ string reason = "exception";
+ int object_id = ret_debugger_cmd_reader.ReadInt32();
+ var caught = ret_debugger_cmd_reader.ReadByte();
+ var exceptionObject = await sdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token);
+ var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("message"));
+ var data = JObject.FromObject(new
+ {
+ type = "object",
+ subtype = "error",
+ className = await sdbHelper.GetClassNameFromObject(sessionId, object_id, token),
+ uncaught = caught == 0,
+ description = exceptionObjectMessage["value"]["value"].Value(),
+ objectId = $"dotnet:object:{object_id}"
+ });
+
+ var ret = await SendCallStack(sessionId, context, reason, thread_id, null, data, args?["callFrames"]?.Values(), token);
+ return ret;
+ }
+ case EventKind.UserBreak:
+ case EventKind.Step:
+ case EventKind.Breakpoint:
+ {
+ Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id);
+ string reason = "other";//other means breakpoint
+ int method_id = 0;
+ if (event_kind != EventKind.UserBreak)
+ method_id = ret_debugger_cmd_reader.ReadInt32();
+ var ret = await SendCallStack(sessionId, context, reason, thread_id, bp, null, args?["callFrames"]?.Values(), token);
+ return ret;
+ }
+ }
+ }
+ return false;
+ }
private async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method_token, SessionId sessionId, CancellationToken token)
{
@@ -826,6 +905,7 @@ private async Task OnResume(MessageId msg_id, CancellationToken token)
}
//discard managed frames
+ sdbHelper.ClearCache();
GetContext(msg_id).ClearState();
}
@@ -838,12 +918,9 @@ private async Task Step(MessageId msg_id, StepKind kind, CancellationToken
if (context.CallStack.Count <= 1 && kind == StepKind.Out)
return false;
- Result res = await SendMonoCommand(msg_id, MonoCommands.StartSingleStepping(kind), token);
-
- int? ret_code = res.Value?["result"]?["value"]?.Value();
-
- if (ret_code.HasValue && ret_code.Value == 0)
- {
+ var step = await sdbHelper.Step(msg_id, context.ThreadId, kind, token);
+ if (step == false) {
+ sdbHelper.ClearCache();
context.ClearState();
await SendCommand(msg_id, "Debugger.stepOut", new JObject(), token);
return false;
@@ -971,24 +1048,21 @@ internal async Task GetScopeProperties(SessionId msg_id, int scope_id, C
return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scope_id}" }));
VarInfo[] var_ids = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);
- Result res = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token);
-
- //if we fail we just buble that to the IDE (and let it panic over it)
- if (res.IsErr)
- return res;
- JObject[] values = res.Value?["result"]?["value"]?.Values().ToArray();
-
- if (values == null || values.Length == 0)
- return Result.OkFromObject(new { result = Array.Empty() });
-
- PerScopeCache frameCache = ctx.GetCacheForScope(scope_id);
- foreach (JObject value in values)
+ var values = await sdbHelper.StackFrameGetValues(msg_id, scope.Method, ctx.ThreadId, scope_id, var_ids, token);
+ if (values != null)
{
- frameCache.Locals[value["name"]?.Value()] = value;
- }
+ if (values == null || values.Count == 0)
+ return Result.OkFromObject(new { result = Array.Empty() });
- return Result.OkFromObject(new { result = values });
+ PerScopeCache frameCache = ctx.GetCacheForScope(scope_id);
+ foreach (JObject value in values)
+ {
+ frameCache.Locals[value["name"]?.Value()] = value;
+ }
+ return Result.OkFromObject(new { result = values });
+ }
+ return Result.OkFromObject(new { result = Array.Empty() });
}
catch (Exception exception)
{
@@ -1004,16 +1078,16 @@ private async Task SetMonoBreakpoint(SessionId sessionId, string req
int method_token = bp.Location.CliLocation.Method.Token;
int il_offset = bp.Location.CliLocation.Offset;
- Result res = await SendMonoCommand(sessionId, MonoCommands.SetBreakpoint(asm_name, method_token, il_offset), token);
- int? ret_code = res.Value?["result"]?["value"]?.Value();
+ var assembly_id = await sdbHelper.GetAssemblyId(sessionId, asm_name, token);
+ var method_id = await sdbHelper.GetMethodIdByToken(sessionId, assembly_id, method_token, token);
+ var breakpoint_id = await sdbHelper.SetBreakpoint(sessionId, method_id, il_offset, token);
- if (ret_code.HasValue)
+ if (breakpoint_id > 0)
{
- bp.RemoteId = ret_code.Value;
+ bp.RemoteId = breakpoint_id;
bp.State = BreakpointState.Active;
//Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
}
-
return bp;
}
@@ -1021,7 +1095,6 @@ private async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, Exe
{
JObject scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
Log("debug", $"sending {source.Url} {context.Id} {sessionId.sessionId}");
-
SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
foreach (var req in context.BreakpointRequests.Values)
@@ -1033,7 +1106,7 @@ private async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, Exe
}
}
- private async Task LoadStore(SessionId sessionId, CancellationToken token)
+ internal async Task LoadStore(SessionId sessionId, CancellationToken token)
{
ExecutionContext context = GetContext(sessionId);
@@ -1072,12 +1145,16 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok
if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource(), null) != null)
return await context.ready.Task;
- Result clear_result = await SendMonoCommand(sessionId, MonoCommands.ClearAllBreakpoints(), token);
- if (clear_result.IsErr)
+ var command_params = new MemoryStream();
+ var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdEventRequest.ClearAllBreakpoints, command_params, token);
+ if (ret_debugger_cmd_reader == null)
{
- Log("verbose", $"Failed to clear breakpoints due to {clear_result}");
+ Log("verbose", $"Failed to clear breakpoints");
}
+ await sdbHelper.SetProtocolVersion(sessionId, token);
+ await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token);
+
DebugStore store = await LoadStore(sessionId, token);
context.ready.SetResult(store);
@@ -1095,10 +1172,8 @@ private async Task RemoveBreakpoint(MessageId msg_id, JObject args, Cancellation
foreach (Breakpoint bp in breakpointRequest.Locations)
{
- Result res = await SendMonoCommand(msg_id, MonoCommands.RemoveBreakpoint(bp.RemoteId), token);
- int? ret_code = res.Value?["result"]?["value"]?.Value();
-
- if (ret_code.HasValue)
+ var breakpoint_removed = await sdbHelper.RemoveBreakpoint(msg_id, bp.RemoteId, token);
+ if (breakpoint_removed)
{
bp.RemoteId = -1;
bp.State = BreakpointState.Disabled;
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
new file mode 100644
index 0000000000000..b3eebcd7e381d
--- /dev/null
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
@@ -0,0 +1,1779 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Net.Http;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.WebAssembly.Diagnostics
+{
+ internal enum TokenType
+ {
+ MdtModule = 0x00000000, //
+ MdtTypeRef = 0x01000000, //
+ MdtTypeDef = 0x02000000, //
+ MdtFieldDef = 0x04000000, //
+ MdtMethodDef = 0x06000000, //
+ MdtParamDef = 0x08000000, //
+ MdtInterfaceImpl = 0x09000000, //
+ MdtMemberRef = 0x0a000000, //
+ MdtCustomAttribute = 0x0c000000, //
+ MdtPermission = 0x0e000000, //
+ MdtSignature = 0x11000000, //
+ MdtEvent = 0x14000000, //
+ MdtProperty = 0x17000000, //
+ MdtModuleRef = 0x1a000000, //
+ MdtTypeSpec = 0x1b000000, //
+ MdtAssembly = 0x20000000, //
+ MdtAssemblyRef = 0x23000000, //
+ MdtFile = 0x26000000, //
+ MdtExportedType = 0x27000000, //
+ MdtManifestResource = 0x28000000, //
+ MdtGenericParam = 0x2a000000, //
+ MdtMethodSpec = 0x2b000000, //
+ MdtGenericParamConstraint = 0x2c000000,
+ MdtString = 0x70000000, //
+ MdtName = 0x71000000, //
+ MdtBaseType = 0x72000000, // Leave this on the high end value. This does not correspond to metadata table
+ }
+
+ internal enum CommandSet {
+ Vm = 1,
+ ObjectRef = 9,
+ StringRef = 10,
+ Thread = 11,
+ ArrayRef = 13,
+ EventRequest = 15,
+ StackFrame = 16,
+ AppDomain = 20,
+ Assembly = 21,
+ Method = 22,
+ Type = 23,
+ Module = 24,
+ Field = 25,
+ Event = 64,
+ Pointer = 65
+ }
+
+ internal enum EventKind {
+ VmStart = 0,
+ VmDeath = 1,
+ ThreadStart = 2,
+ ThreadDeath = 3,
+ AppDomainCreate = 4,
+ AppDomainUnload = 5,
+ MethodEntry = 6,
+ MethodExit = 7,
+ AssemblyLoad = 8,
+ AssemblyUnload = 9,
+ Breakpoint = 10,
+ Step = 11,
+ TypeLoad = 12,
+ Exception = 13,
+ KeepAlive = 14,
+ UserBreak = 15,
+ UserLog = 16,
+ Crash = 17
+ }
+
+ internal enum ModifierKind {
+ Count = 1,
+ ThreadOnly = 3,
+ LocationOnly = 7,
+ ExceptionOnly = 8,
+ Step = 10,
+ AssemblyOnly = 11,
+ SourceFileOnly = 12,
+ TypeNameOnly = 13
+ }
+
+
+ internal enum SuspendPolicy {
+ None = 0,
+ EventThread = 1,
+ All = 2
+ }
+
+ internal enum CmdVM {
+ Version = 1,
+ AllThreads = 2,
+ Suspend = 3,
+ Resume = 4,
+ Exit = 5,
+ Dispose = 6,
+ InvokeMethod = 7,
+ SetProtocolVersion = 8,
+ AbortInvoke = 9,
+ SetKeepAlive = 10,
+ GetTypesForSourceFile = 11,
+ GetTypes = 12,
+ InvokeMethods = 13,
+ StartBuffering = 14,
+ StopBuffering = 15,
+ VmReadMemory = 16,
+ VmWriteMemory = 17,
+ GetAssemblyByName = 18
+ }
+
+ internal enum CmdFrame {
+ GetValues = 1,
+ GetThis = 2,
+ SetValues = 3,
+ GetDomain = 4,
+ SetThis = 5,
+ GetArgument = 6,
+ GetArguments = 7
+ }
+
+ internal enum CmdEvent {
+ Composite = 100
+ }
+
+ internal enum CmdThread {
+ GetFrameInfo = 1,
+ GetName = 2,
+ GetState = 3,
+ GetInfo = 4,
+ /* FIXME: Merge into GetInfo when the major protocol version is increased */
+ GetId = 5,
+ /* Ditto */
+ GetTid = 6,
+ SetIp = 7,
+ GetElapsedTime = 8
+ }
+
+ internal enum CmdEventRequest {
+ Set = 1,
+ Clear = 2,
+ ClearAllBreakpoints = 3
+ }
+
+ internal enum CmdAppDomain {
+ GetRootDomain = 1,
+ GetFriendlyName = 2,
+ GetAssemblies = 3,
+ GetEntryAssembly = 4,
+ CreateString = 5,
+ GetCorLib = 6,
+ CreateBoxedValue = 7,
+ CreateByteArray = 8,
+ }
+
+ internal enum CmdAssembly {
+ GetLocation = 1,
+ GetEntryPoint = 2,
+ GetManifestModule = 3,
+ GetObject = 4,
+ GetType = 5,
+ GetName = 6,
+ GetDomain = 7,
+ GetMetadataBlob = 8,
+ GetIsDynamic = 9,
+ GetPdbBlob = 10,
+ GetTypeFromToken = 11,
+ GetMethodFromToken = 12,
+ HasDebugInfo = 13,
+ }
+
+ internal enum CmdModule {
+ GetInfo = 1,
+ ApplyChanges = 2,
+ }
+
+ internal enum CmdPointer{
+ GetValue = 1
+ }
+
+ internal enum CmdMethod {
+ GetName = 1,
+ GetDeclaringType = 2,
+ GetDebugInfo = 3,
+ GetParamInfo = 4,
+ GetLocalsInfo = 5,
+ GetInfo = 6,
+ GetBody = 7,
+ ResolveToken = 8,
+ GetCattrs = 9,
+ MakeGenericMethod = 10,
+ Token = 11,
+ Assembly = 12,
+ ClassToken = 13,
+ AsyncDebugInfo = 14,
+ GetNameFull = 15
+ }
+
+ internal enum CmdType {
+ GetInfo = 1,
+ GetMethods = 2,
+ GetFields = 3,
+ GetValues = 4,
+ GetObject = 5,
+ GetSourceFiles = 6,
+ SetValues = 7,
+ IsAssignableFrom = 8,
+ GetProperties = 9,
+ GetCattrs = 10,
+ GetFieldCattrs = 11,
+ GetPropertyCattrs = 12,
+ /* FIXME: Merge into GetSourceFiles when the major protocol version is increased */
+ GetSourceFiles2 = 13,
+ /* FIXME: Merge into GetValues when the major protocol version is increased */
+ GetValues2 = 14,
+ GetMethodsByNameFlags = 15,
+ GetInterfaces = 16,
+ GetInterfacesMap = 17,
+ IsInitialized = 18,
+ CreateInstance = 19,
+ GetValueSize = 20,
+ GetValuesICorDbg = 21,
+ GetParents = 22
+ }
+
+ internal enum CmdArray {
+ GetLength = 1,
+ GetValues = 2,
+ SetValues = 3,
+ RefGetType = 4
+ }
+
+
+ internal enum CmdField {
+ GetInfo = 1
+ }
+
+ internal enum CmdString {
+ GetValue = 1,
+ GetLength = 2,
+ GetChars = 3
+ }
+
+ internal enum CmdObject {
+ RefGetType = 1,
+ RefGetValues = 2,
+ RefIsCollected = 3,
+ RefGetAddress = 4,
+ RefGetDomain = 5,
+ RefSetValues = 6,
+ RefGetInfo = 7,
+ GetValuesICorDbg = 8,
+ RefDelegateGetMethod = 9,
+ RefIsDelegate = 10
+ }
+
+ internal enum ElementType {
+ End = 0x00,
+ Void = 0x01,
+ Boolean = 0x02,
+ Char = 0x03,
+ I1 = 0x04,
+ U1 = 0x05,
+ I2 = 0x06,
+ U2 = 0x07,
+ I4 = 0x08,
+ U4 = 0x09,
+ I8 = 0x0a,
+ U8 = 0x0b,
+ R4 = 0x0c,
+ R8 = 0x0d,
+ String = 0x0e,
+ Ptr = 0x0f,
+ ByRef = 0x10,
+ ValueType = 0x11,
+ Class = 0x12,
+ Var = 0x13,
+ Array = 0x14,
+ GenericInst = 0x15,
+ TypedByRef = 0x16,
+ I = 0x18,
+ U = 0x19,
+ FnPtr = 0x1b,
+ Object = 0x1c,
+ SzArray = 0x1d,
+ MVar = 0x1e,
+ CModReqD = 0x1f,
+ CModOpt = 0x20,
+ Internal = 0x21,
+ Modifier = 0x40,
+ Sentinel = 0x41,
+ Pinned = 0x45,
+
+ Type = 0x50,
+ Boxed = 0x51,
+ Enum = 0x55
+ }
+
+ internal enum ValueTypeId {
+ Null = 0xf0,
+ Type = 0xf1,
+ VType = 0xf2,
+ FixedArray = 0xf3
+ }
+ internal enum MonoTypeNameFormat{
+ FormatIL,
+ FormatReflection,
+ FullName,
+ AssemblyQualified
+ }
+
+ internal enum StepFilter {
+ None = 0,
+ StaticCtor = 1,
+ DebuggerHidden = 2,
+ DebuggerStepThrough = 4,
+ DebuggerNonUserCode = 8
+ }
+
+ internal class MonoBinaryReader : BinaryReader
+ {
+ public MonoBinaryReader(Stream stream) : base(stream) {}
+
+ internal static unsafe void PutBytesBE (byte *dest, byte *src, int count)
+ {
+ int i = 0;
+
+ if (BitConverter.IsLittleEndian){
+ dest += count;
+ for (; i < count; i++)
+ *(--dest) = *src++;
+ } else {
+ for (; i < count; i++)
+ *dest++ = *src++;
+ }
+ }
+
+ public override string ReadString()
+ {
+ var valueLen = ReadInt32();
+ char[] value = new char[valueLen];
+ Read(value, 0, valueLen);
+ return new string(value);
+ }
+ public unsafe long ReadLong()
+ {
+ byte[] data = new byte[8];
+ Read(data, 0, 8);
+
+ long ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 8);
+ }
+
+ return ret;
+ }
+ public override unsafe sbyte ReadSByte()
+ {
+ byte[] data = new byte[4];
+ Read(data, 0, 4);
+
+ int ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 4);
+ }
+ return (sbyte)ret;
+ }
+
+ public unsafe byte ReadUByte()
+ {
+ byte[] data = new byte[4];
+ Read(data, 0, 4);
+
+ int ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 4);
+ }
+ return (byte)ret;
+ }
+
+ public override unsafe int ReadInt32()
+ {
+ byte[] data = new byte[4];
+ Read(data, 0, 4);
+ int ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 4);
+ }
+ return ret;
+ }
+
+ public override unsafe double ReadDouble()
+ {
+ byte[] data = new byte[8];
+ Read(data, 0, 8);
+
+ double ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 8);
+ }
+ return ret;
+ }
+
+ public override unsafe uint ReadUInt32()
+ {
+ byte[] data = new byte[4];
+ Read(data, 0, 4);
+
+ uint ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 4);
+ }
+ return ret;
+ }
+ public unsafe ushort ReadUShort()
+ {
+ byte[] data = new byte[4];
+ Read(data, 0, 4);
+
+ uint ret;
+ fixed (byte *src = &data[0]){
+ PutBytesBE ((byte *) &ret, src, 4);
+ }
+ return (ushort)ret;
+ }
+ }
+
+ internal class MonoBinaryWriter : BinaryWriter
+ {
+ public MonoBinaryWriter(Stream stream) : base(stream) {}
+ public void WriteString(string val)
+ {
+ Write(val.Length);
+ Write(val.ToCharArray());
+ }
+ public void WriteLong(long val)
+ {
+ Write((int)((val >> 32) & 0xffffffff));
+ Write((int)((val >> 0) & 0xffffffff));
+ }
+ public override void Write(int val)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Array.Reverse(bytes, 0, bytes.Length);
+ Write(bytes);
+ }
+ public void WriteObj(DotnetObjectId objectId, MonoSDBHelper sdbHelper)
+ {
+ if (objectId.Scheme == "object")
+ {
+ Write((byte)ElementType.Class);
+ Write(int.Parse(objectId.Value));
+ }
+ if (objectId.Scheme == "valuetype")
+ {
+ Write(sdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer);
+ }
+ }
+ }
+ internal class FieldTypeClass
+ {
+ public int Id { get; }
+ public string Name { get; }
+ public int TypeId { get; }
+ public FieldTypeClass(int id, string name, int typeId)
+ {
+ Id = id;
+ Name = name;
+ TypeId = typeId;
+ }
+ }
+ internal class ValueTypeClass
+ {
+ public byte[] valueTypeBuffer;
+ public JArray valueTypeJson;
+ public JArray valueTypeJsonProps;
+ public int typeId;
+ public JArray valueTypeProxy;
+ public string valueTypeVarName;
+ public bool valueTypeAutoExpand;
+ public int Id;
+ public ValueTypeClass(string varName, byte[] buffer, JArray json, int id, bool expand_properties, int valueTypeId)
+ {
+ valueTypeBuffer = buffer;
+ valueTypeJson = json;
+ typeId = id;
+ valueTypeJsonProps = null;
+ valueTypeProxy = null;
+ valueTypeVarName = varName;
+ valueTypeAutoExpand = expand_properties;
+ Id = valueTypeId;
+ }
+ }
+ internal class PointerValue
+ {
+ public long address;
+ public int typeId;
+ public string varName;
+ public PointerValue(long address, int typeId, string varName)
+ {
+ this.address = address;
+ this.typeId = typeId;
+ this.varName = varName;
+ }
+
+ }
+ internal class MonoSDBHelper
+ {
+ internal Dictionary valueTypes = new Dictionary();
+ internal Dictionary pointerValues = new Dictionary();
+ private static int debugger_object_id;
+ private static int cmd_id;
+ private static int GetId() {return cmd_id++;}
+ private MonoProxy proxy;
+ private static int MINOR_VERSION = 61;
+ private static int MAJOR_VERSION = 2;
+ public MonoSDBHelper(MonoProxy proxy)
+ {
+ this.proxy = proxy;
+ }
+
+ public void ClearCache()
+ {
+ valueTypes = new Dictionary();
+ pointerValues = new Dictionary();
+ }
+
+ public async Task SetProtocolVersion(SessionId sessionId, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(MAJOR_VERSION);
+ command_params_writer.Write(MINOR_VERSION);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token);
+ return true;
+ }
+ public async Task EnableReceiveUserBreakRequest(SessionId sessionId, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((byte)EventKind.UserBreak);
+ command_params_writer.Write((byte)SuspendPolicy.None);
+ command_params_writer.Write((byte)0);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token);
+ return true;
+ }
+ internal async Task SendDebuggerAgentCommandInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, CancellationToken token)
+ {
+ Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommand(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray())), token);
+ if (res.IsErr) {
+ throw new Exception($"SendDebuggerAgentCommand Error - {(CommandSet)command_set} - {command}");
+ }
+ byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value());
+ var ret_debugger_cmd = new MemoryStream(newBytes);
+ var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd);
+ return ret_debugger_cmd_reader;
+ }
+
+ internal CommandSet GetCommandSetForCommand(T command) =>
+ command switch {
+ CmdVM => CommandSet.Vm,
+ CmdObject => CommandSet.ObjectRef,
+ CmdString => CommandSet.StringRef,
+ CmdThread => CommandSet.Thread,
+ CmdArray => CommandSet.ArrayRef,
+ CmdEventRequest => CommandSet.EventRequest,
+ CmdFrame => CommandSet.StackFrame,
+ CmdAppDomain => CommandSet.AppDomain,
+ CmdAssembly => CommandSet.Assembly,
+ CmdMethod => CommandSet.Method,
+ CmdType => CommandSet.Type,
+ CmdModule => CommandSet.Module,
+ CmdField => CommandSet.Field,
+ CmdEvent => CommandSet.Event,
+ CmdPointer => CommandSet.Pointer,
+ _ => throw new Exception ("Unknown CommandSet")
+ };
+
+ internal Task SendDebuggerAgentCommand(SessionId sessionId, T command, MemoryStream parms, CancellationToken token) where T : Enum =>
+ SendDebuggerAgentCommandInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, token);
+
+ internal Task SendDebuggerAgentCommandWithParms(SessionId sessionId, T command, MemoryStream parms, int type, string extraParm, CancellationToken token) where T : Enum =>
+ SendDebuggerAgentCommandWithParmsInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, type, extraParm, token);
+
+ internal async Task SendDebuggerAgentCommandWithParmsInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, int type, string extraParm, CancellationToken token)
+ {
+ Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommandWithParms(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray()), parms.ToArray().Length, type, extraParm), token);
+ if (res.IsErr) {
+ throw new Exception("SendDebuggerAgentCommandWithParms Error");
+ }
+ byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value());
+ var ret_debugger_cmd = new MemoryStream(newBytes);
+ var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd);
+ return ret_debugger_cmd_reader;
+ }
+
+ public async Task GetMethodToken(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Token, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32() & 0xffffff; //token
+ }
+
+ public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, int method_token, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(assembly_id);
+ command_params_writer.Write(method_token | (int)TokenType.MdtMethodDef);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetMethodFromToken, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32();
+ }
+
+ public async Task GetAssemblyIdFromMethod(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Assembly, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32(); //assembly_id
+ }
+
+ public async Task GetAssemblyId(SessionId sessionId, string asm_name, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.WriteString(asm_name);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetAssemblyByName, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32();
+ }
+
+ public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(assembly_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetLocation, command_params, token);
+ return ret_debugger_cmd_reader.ReadString();
+ }
+
+
+ public async Task GetAssemblyNameFull(SessionId sessionId, int assembly_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(assembly_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, command_params, token);
+ var name = ret_debugger_cmd_reader.ReadString();
+ return name.Remove(name.IndexOf(",")) + ".dll";
+ }
+
+ public async Task GetMethodName(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetNameFull, command_params, token);
+ var methodName = ret_debugger_cmd_reader.ReadString();
+ return methodName.Substring(methodName.IndexOf(":")+1);
+ }
+
+ public async Task MethodIsStatic(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetInfo, command_params, token);
+ var flags = ret_debugger_cmd_reader.ReadInt32();
+ return (flags & 0x0010) > 0; //check method is static
+ }
+
+ public async Task GetParamCount(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token);
+ ret_debugger_cmd_reader.ReadInt32();
+ int param_count = ret_debugger_cmd_reader.ReadInt32();
+ return param_count;
+ }
+
+ public async Task GetReturnType(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token);
+ ret_debugger_cmd_reader.ReadInt32();
+ ret_debugger_cmd_reader.ReadInt32();
+ ret_debugger_cmd_reader.ReadInt32();
+ var retType = ret_debugger_cmd_reader.ReadInt32();
+ var ret = await GetTypeName(sessionId, retType, token);
+ return ret;
+ }
+
+ public async Task GetParameters(SessionId sessionId, int method_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(method_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token);
+ ret_debugger_cmd_reader.ReadInt32();
+ var paramCount = ret_debugger_cmd_reader.ReadInt32();
+ ret_debugger_cmd_reader.ReadInt32();
+ var retType = ret_debugger_cmd_reader.ReadInt32();
+ var parameters = "(";
+ for (int i = 0 ; i < paramCount; i++)
+ {
+ var paramType = ret_debugger_cmd_reader.ReadInt32();
+ parameters += await GetTypeName(sessionId, paramType, token);
+ parameters = parameters.Replace("System.Func", "Func");
+ if (i + 1 < paramCount)
+ parameters += ",";
+ }
+ parameters += ")";
+ return parameters;
+ }
+
+ public async Task SetBreakpoint(SessionId sessionId, int method_id, long il_offset, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((byte)EventKind.Breakpoint);
+ command_params_writer.Write((byte)SuspendPolicy.None);
+ command_params_writer.Write((byte)1);
+ command_params_writer.Write((byte)ModifierKind.LocationOnly);
+ command_params_writer.Write(method_id);
+ command_params_writer.WriteLong(il_offset);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32();
+ }
+
+ public async Task RemoveBreakpoint(SessionId sessionId, int breakpoint_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((byte)EventKind.Breakpoint);
+ command_params_writer.Write((int) breakpoint_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token);
+
+ if (ret_debugger_cmd_reader != null)
+ return true;
+ return false;
+ }
+
+ public async Task Step(SessionId sessionId, int thread_id, StepKind kind, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((byte)EventKind.Step);
+ command_params_writer.Write((byte)SuspendPolicy.None);
+ command_params_writer.Write((byte)1);
+ command_params_writer.Write((byte)ModifierKind.Step);
+ command_params_writer.Write(thread_id);
+ command_params_writer.Write((int)0);
+ command_params_writer.Write((int)kind);
+ command_params_writer.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token);
+ if (ret_debugger_cmd_reader == null)
+ return false;
+ var isBPOnManagedCode = ret_debugger_cmd_reader.ReadInt32();
+ if (isBPOnManagedCode == 0)
+ return false;
+ return true;
+ }
+
+ public async Task ClearSingleStep(SessionId sessionId, int req_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((byte)EventKind.Step);
+ command_params_writer.Write((int) req_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token);
+
+ if (ret_debugger_cmd_reader != null)
+ return true;
+ return false;
+ }
+
+ public async Task> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token)
+ {
+ var ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(type_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetFields, command_params, token);
+ var nFields = ret_debugger_cmd_reader.ReadInt32();
+
+ for (int i = 0 ; i < nFields; i++)
+ {
+ int fieldId = ret_debugger_cmd_reader.ReadInt32(); //fieldId
+ string fieldNameStr = ret_debugger_cmd_reader.ReadString();
+ int typeId = ret_debugger_cmd_reader.ReadInt32(); //typeId
+ ret_debugger_cmd_reader.ReadInt32(); //attrs
+ if (fieldNameStr.Contains("k__BackingField"))
+ {
+ fieldNameStr = fieldNameStr.Replace("k__BackingField", "");
+ fieldNameStr = fieldNameStr.Replace("<", "");
+ fieldNameStr = fieldNameStr.Replace(">", "");
+ }
+ ret.Add(new FieldTypeClass(fieldId, fieldNameStr, typeId));
+ }
+ return ret;
+ }
+ public string ReplaceCommonClassNames(string className)
+ {
+ className = className.Replace("System.String", "string");
+ className = className.Replace("System.Boolean", "bool");
+ className = className.Replace("System.Char", "char");
+ className = className.Replace("System.Int32", "int");
+ className = className.Replace("System.Object", "object");
+ className = className.Replace("System.Void", "void");
+ className = className.Replace("System.Byte", "byte");
+ return className;
+ }
+ public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(type_id);
+ command_params_writer.Write((int) MonoTypeNameFormat.FormatReflection);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, command_params, token);
+
+ ret_debugger_cmd_reader.ReadString();
+
+ ret_debugger_cmd_reader.ReadString();
+
+ string className = ret_debugger_cmd_reader.ReadString();
+
+ className = className.Replace("+", ".");
+ className = Regex.Replace(className, @"`\d+", "");
+ className = className.Replace("[]", "__SQUARED_BRACKETS__");
+ className = className.Replace("[", "<");
+ className = className.Replace("]", ">");
+ className = className.Replace("__SQUARED_BRACKETS__", "[]");
+ className = className.Replace(",", ", ");
+ className = ReplaceCommonClassNames(className);
+ return className;
+ }
+
+ public async Task GetStringValue(SessionId sessionId, int string_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(string_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdString.GetValue, command_params, token);
+ var isUtf16 = ret_debugger_cmd_reader.ReadByte();
+ if (isUtf16 == 0) {
+ return ret_debugger_cmd_reader.ReadString();
+ }
+ return null;
+ }
+ public async Task GetArrayLength(SessionId sessionId, int object_id, CancellationToken token)
+ {
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(object_id);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetLength, command_params, token);
+ var length = ret_debugger_cmd_reader.ReadInt32();
+ length = ret_debugger_cmd_reader.ReadInt32();
+ return length;
+ }
+ public async Task> GetTypeIdFromObject(SessionId sessionId, int object_id, bool withParents, CancellationToken token)
+ {
+ List ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(object_id);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetType, command_params, token);
+ var type_id = ret_debugger_cmd_reader.ReadInt32();
+ ret.Add(type_id);
+ if (withParents)
+ {
+ command_params = new MemoryStream();
+ command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(type_id);
+ ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token);
+ var parentsCount = ret_debugger_cmd_reader.ReadInt32();
+ for (int i = 0 ; i < parentsCount; i++)
+ {
+ ret.Add(ret_debugger_cmd_reader.ReadInt32());
+ }
+ }
+ return ret;
+ }
+
+ public async Task GetClassNameFromObject(SessionId sessionId, int object_id, CancellationToken token)
+ {
+ var type_id = await GetTypeIdFromObject(sessionId, object_id, false, token);
+ return await GetTypeName(sessionId, type_id[0], token);
+ }
+
+ public async Task GetMethodIdByName(SessionId sessionId, int type_id, string method_name, CancellationToken token)
+ {
+ var ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((int)type_id);
+ command_params_writer.WriteString(method_name);
+ command_params_writer.Write((int)(0x10 | 4)); //instance methods
+ command_params_writer.Write((int)1); //case sensitive
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, command_params, token);
+ var nMethods = ret_debugger_cmd_reader.ReadInt32();
+ return ret_debugger_cmd_reader.ReadInt32();
+ }
+
+ public async Task IsDelegate(SessionId sessionId, int objectId, CancellationToken token)
+ {
+ var ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((int)objectId);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefIsDelegate, command_params, token);
+ return ret_debugger_cmd_reader.ReadByte() == 1;
+ }
+
+ public async Task GetDelegateMethod(SessionId sessionId, int objectId, CancellationToken token)
+ {
+ var ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write((int)objectId);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefDelegateGetMethod, command_params, token);
+ return ret_debugger_cmd_reader.ReadInt32();
+ }
+
+ public async Task GetDelegateMethodDescription(SessionId sessionId, int objectId, CancellationToken token)
+ {
+ var methodId = await GetDelegateMethod(sessionId, objectId, token);
+
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(methodId);
+ //Console.WriteLine("methodId - " + methodId);
+ if (methodId == 0)
+ return "";
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetName, command_params, token);
+ var methodName = ret_debugger_cmd_reader.ReadString();
+
+ var returnType = await GetReturnType(sessionId, methodId, token);
+ var parameters = await GetParameters(sessionId, methodId, token);
+
+ return $"{returnType} {methodName} {parameters}";
+ }
+ public async Task InvokeMethod(SessionId sessionId, byte[] valueTypeBuffer, int method_id, string varName, CancellationToken token)
+ {
+ MemoryStream parms = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(parms);
+ command_params_writer.Write(method_id);
+ command_params_writer.Write(valueTypeBuffer);
+ command_params_writer.Write(0);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.InvokeMethod, parms, token);
+ ret_debugger_cmd_reader.ReadByte(); //number of objects returned.
+ return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, varName, false, -1, token);
+ }
+ public async Task CreateJArrayForProperties(SessionId sessionId, int typeId, byte[] object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token)
+ {
+ JArray ret = new JArray();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(typeId);
+
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token);
+ var nProperties = ret_debugger_cmd_reader.ReadInt32();
+ for (int i = 0 ; i < nProperties; i++)
+ {
+ ret_debugger_cmd_reader.ReadInt32(); //propertyId
+ string propertyNameStr = ret_debugger_cmd_reader.ReadString();
+ var getMethodId = ret_debugger_cmd_reader.ReadInt32();
+ ret_debugger_cmd_reader.ReadInt32(); //setmethod
+ var attrs = ret_debugger_cmd_reader.ReadInt32(); //attrs
+ if (getMethodId == 0 || await GetParamCount(sessionId, getMethodId, token) != 0 || await MethodIsStatic(sessionId, getMethodId, token))
+ continue;
+ JObject propRet = null;
+ if (attributes.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any())
+ continue;
+ if (isAutoExpandable)
+ {
+ try {
+ propRet = await InvokeMethod(sessionId, object_buffer, getMethodId, propertyNameStr, token);
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ propRet = JObject.FromObject(new {
+ get = new
+ {
+ type = "function",
+ objectId = $"{objectId}:method_id:{getMethodId}",
+ className = "Function",
+ description = "get " + propertyNameStr + " ()",
+ methodId = getMethodId,
+ objectIdValue = objectId
+ },
+ name = propertyNameStr
+ });
+ }
+ if (isOwn)
+ propRet["isOwn"] = true;
+ ret.Add(propRet);
+ }
+ return ret;
+ }
+ public async Task GetPointerContent(SessionId sessionId, int pointerId, CancellationToken token)
+ {
+ var ret = new List();
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.WriteLong(pointerValues[pointerId].address);
+ command_params_writer.Write(pointerValues[pointerId].typeId);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdPointer.GetValue, command_params, token);
+ var varName = pointerValues[pointerId].varName;
+ if (int.TryParse(varName, out _))
+ varName = $"[{varName}]";
+ return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "*" + varName, false, -1, token);
+ }
+ public async Task GetPropertiesValuesOfValueType(SessionId sessionId, int valueTypeId, CancellationToken token)
+ {
+ JArray ret = new JArray();
+ var valueType = valueTypes[valueTypeId];
+ var command_params = new MemoryStream();
+ var command_params_writer = new MonoBinaryWriter(command_params);
+ command_params_writer.Write(valueType.typeId);
+ var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token);
+ var parentsCount = ret_debugger_cmd_reader.ReadInt32();
+ List typesToGetProperties = new List();
+ typesToGetProperties.Add(valueType.typeId);
+ for (int i = 0 ; i < parentsCount; i++)
+ {
+ typesToGetProperties.Add(ret_debugger_cmd_reader.ReadInt32());
+ }
+ for (int i = 0 ; i < typesToGetProperties.Count; i++)
+ {
+ var properties = await CreateJArrayForProperties(sessionId, typesToGetProperties[i], valueType.valueTypeBuffer, valueType.valueTypeJson, valueType.valueTypeAutoExpand, $"dotnet:valuetype:{valueType.Id}", i == 0, token);
+ ret = new JArray(ret.Union(properties));
+ }
+
+ return ret;
+ }
+
+ public bool AutoExpandable(string className) {
+ if (className == "System.DateTime" ||
+ className == "System.DateTimeOffset" ||
+ className == "System.TimeSpan")
+ return true;
+ return false;
+ }
+
+ public bool AutoInvokeToString(string className) {
+ if (className == "System.DateTime" ||
+ className == "System.DateTimeOffset" ||
+ className == "System.TimeSpan" ||
+ className == "System.Decimal" ||
+ className == "System.Guid")
+ return true;
+ return false;
+ }
+
+ public JObject CreateJObject(T value, string type, string description, bool writable, string className = null, string objectId = null, string __custom_type = null, string subtype = null, bool isValueType = false, bool expanded = false, bool isEnum = false)
+ {
+ var ret = JObject.FromObject(new {
+ value = new
+ {
+ type,
+ value,
+ description
+ },
+ writable
+ });
+ if (__custom_type != null)
+ ret["value"]["__custom_type"] = __custom_type;
+ if (className != null)
+ ret["value"]["className"] = className;
+ if (objectId != null)
+ ret["value"]["objectId"] = objectId;
+ if (subtype != null)
+ ret["value"]["subtype"] = subtype;
+ if (isValueType)
+ ret["value"]["isValueType"] = isValueType;
+ if (expanded)
+ ret["value"]["expanded"] = expanded;
+ if (isEnum)
+ ret["value"]["isEnum"] = isEnum;
+ return ret;
+
+ }
+ public JObject CreateJObjectForBoolean(int value)
+ {
+ return CreateJObject(value == 0 ? false : true, "boolean", value == 0 ? "false" : "true", true);
+ }
+
+ public JObject CreateJObjectForNumber(T value)
+ {
+ return CreateJObject